CodeGym /Cours /JAVA 25 SELF /List.of, Set.of, Map.of — collections immuables

List.of, Set.of, Map.of — collections immuables

JAVA 25 SELF
Niveau 34 , Leçon 0
Disponible

1. Introduction

Collections classiques : flexibilité et pièges

Lorsque vous créez une collection avec new ArrayList<>(), vous obtenez une structure que vous pouvez modifier librement : ajouter, supprimer, remplacer des éléments. C’est pratique quand vous construisez des données « à la volée ». Mais que se passe‑t‑il si vous passez cette collection à une autre classe ou méthode où elle ne doit pas être modifiée ? Et si vous la transmettez par inadvertance à l’extérieur et que quelqu’un la modifie ? C’est là que commencent les problèmes.

Exemple d’erreur classique

import java.util.*;

public class Example {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");

        // On transmet la collection "à l'extérieur"
        processNames(names);

        // On s'attend à ce que la liste n'ait pas changé...
        System.out.println(names);
    }

    public static void processNames(List<String> list) {
        // Et quelqu'un a supprimé un élément !
        list.remove("Bob");
    }
}

Sortie :

[Alice, Charlie]

Votre collection a changé alors que vous ne l’aviez absolument pas prévu. Dans les grands projets, de telles « surprises » se transforment facilement en bugs très désagréables et difficiles à traquer.

Le danger, c’est que le code travaillant avec la collection peut se heurter soudainement à des modifications de données imprévisibles. Il y a aussi un risque permanent de perte d’information — quelqu’un a supprimé un élément par accident ou l’a écrasé. Et si la collection est modifiée simultanément depuis plusieurs threads, vous pouvez vous retrouver non seulement avec une ConcurrentModificationException, mais aussi avec un problème encore plus insidieux — des données incohérentes.

Protection des collections : ancienne approche

Avant Java 9, on utilisait des méthodes‑wrappers comme Collections.unmodifiableList(...), dont nous avons parlé au chapitre précédent. Elles permettent de renvoyer une collection « gelée ». Mais cette approche n’est pas toujours pratique et ne résout pas tous les problèmes (nous y reviendrons en détail dans la prochaine leçon).

2. Solution moderne : méthodes de fabrique List.of, Set.of, Map.of

Dans Java 9, de nouvelles méthodes statiques sont apparues dans les interfaces des collections : List.of, Set.of, Map.of. Elles permettent de créer rapidement et facilement une collection qui ne peut pas être modifiée. C’est comme si vous aviez fabriqué une collection et l’aviez aussitôt coulée dans le béton — personne ne pourra ajouter, supprimer ou modifier des éléments.

Exemple de création de collections immuables

import java.util.*;

public class ImmutableDemo {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob", "Charlie");
        Set<Integer> numbers = Set.of(1, 2, 3);
        Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25, "Charlie", 28);

        System.out.println(names);
        System.out.println(numbers);
        System.out.println(ages);
    }
}

Sortie :

[Alice, Bob, Charlie]
[1, 2, 3]
{Alice=30, Bob=25, Charlie=28}

Comment ça marche ?

  • List.of(...) — crée une liste immuable.
  • Set.of(...) — crée un ensemble immuable.
  • Map.of(...) — crée une map immuable (jusqu’à 10 paires clé‑valeur ; pour davantage, utilisez Map.ofEntries(...)).

Attention ! Les collections créées par ces méthodes ne tolèrent aucune modification. Toute tentative d’ajouter, de supprimer ou de remplacer un élément provoquera une exception.

3. Exemples d’utilisation et « pièges »

Exemple : tentative de modifier la collection

import java.util.*;

public class ImmutableFail {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob");
        // names.add("Charlie"); // Erreur à l'exécution !
        try {
            names.add("Charlie");
        } catch (UnsupportedOperationException ex) {
            System.out.println("Impossible d'ajouter l'élément: " + ex.getClass().getSimpleName());
        }
    }
}

Sortie :

Impossible d'ajouter l'élément: UnsupportedOperationException

Exemple : tentative d’ajouter null

import java.util.*;

public class NullFail {
    public static void main(String[] args) {
        try {
            List<String> badList = List.of("Alice", null, "Bob");
        } catch (NullPointerException ex) {
            System.out.println("Null interdit: " + ex.getClass().getSimpleName());
        }
    }
}

Sortie :

Null interdit: NullPointerException

Exemple : doublons dans Set.of

import java.util.*;

public class DuplicatesFail {
    public static void main(String[] args) {
        try {
            Set<String> badSet = Set.of("one", "two", "one");
        } catch (IllegalArgumentException ex) {
            System.out.println("Doublons interdits: " + ex.getClass().getSimpleName());
        }
    }
}

Sortie :

Doublons interdits: IllegalArgumentException

Exemple : Map.of avec un grand nombre de paires

import java.util.*;

public class MapOfLarge {
    public static void main(String[] args) {
        // Map.of prend en charge jusqu'à 10 paires clé-valeur
        Map<String, Integer> map = Map.of(
            "one", 1, "two", 2, "three", 3, "four", 4, "five", 5,
            "six", 6, "seven", 7, "eight", 8, "nine", 9, "ten", 10
        );
        System.out.println(map);

        // Pour davantage, utilisez Map.ofEntries
        Map<String, Integer> bigMap = Map.ofEntries(
            Map.entry("eleven", 11),
            Map.entry("twelve", 12),
            Map.entry("thirteen", 13)
            // ... etc.
        );
        System.out.println(bigMap);
    }
}

4. Caractéristiques et limitations des collections immuables

Aucune modification possible.
Toute tentative d’ajouter, de supprimer ou de modifier un élément provoquera une UnsupportedOperationException. Même les méthodes habituellement autorisées (add, remove, set) ne fonctionnent pas.

null interdit.
Si vous essayez d’ajouter null comme élément de liste ou d’ensemble, ou comme clé/valeur dans une map, vous obtiendrez une NullPointerException. C’est pour la sécurité : les éléments null entraînent souvent des erreurs dans les collections.

Implémentation concrète non garantie.
Vous ne saurez pas quelle classe exacte se cache derrière une collection créée via List.of et consorts. Évitez de faire instanceof ArrayList ou d’essayer de caster la collection vers un type concret.

Ordre des éléments.
— Avec List.of, l’ordre des éléments est préservé (comme dans une liste normale).
— Avec Set.of, l’ordre n’est pas garanti (en pratique, il peut correspondre à l’ordre des arguments, mais il vaut mieux ne pas s’y fier).
— Avec Map.of, l’ordre des paires n’est pas garanti.

Performances.
Les collections créées via les méthodes de fabrique sont généralement plus rapides que les wrappers autour des collections mutables, car elles n’utilisent pas de mémoire pour des capacités inutiles.

5. Quand et pourquoi utiliser des collections immuables

Ensembles de données constants

Si vous avez une liste, un ensemble ou une map qui ne doivent pas changer pendant l’exécution, utilisez List.of, Set.of, Map.of. Par exemple :

private static final List<String> ROLES = List.of("USER", "ADMIN", "MODERATOR");

Personne ne pourra ajouter un rôle superflu à cette liste.

Retourner des collections depuis des méthodes

Si vous retournez une collection depuis une méthode et ne voulez pas qu’elle soit modifiée de l’extérieur :

public List<String> getDefaultNames() {
    return List.of("Alice", "Bob", "Charlie");
}

Le destinataire ne pourra pas altérer vos données.

Passage entre couches de l’application

Lorsque vous transmettez des collections entre différentes parties du programme (par exemple entre les couches Controller et Service dans une application web), il est préférable d’utiliser des collections immuables pour que personne ne puisse les modifier « en douce ».

Sécurité et sûreté des threads

Les collections immuables sont par définition thread‑safe en lecture : si personne ne peut les modifier, elles peuvent être utilisées depuis plusieurs threads sans synchronisation.

6. Exemples pratiques pour une application générique

Supposons que notre application d’apprentissage comporte une liste de commandes prises en charge :

public class Commands {
    public static final List<String> SUPPORTED_COMMANDS = List.of(
        "help", "exit", "list", "add", "remove"
    );
}

Si vous essayez de faire ceci :

Commands.SUPPORTED_COMMANDS.add("hack_the_system");

Vous obtiendrez une exception et ne pourrez pas nuire à l’application.

Ou, par exemple, si vous avez une map avec des codes d’erreur :

public class ErrorCodes {
    public static final Map<Integer, String> CODES = Map.of(
        404, "Not Found",
        500, "Internal Server Error",
        403, "Forbidden"
    );
}

Toute tentative d’ajouter un nouveau code — provoquera une exception.

7. Comparaison des approches de création de collections

Méthode de création Modifiable ? null autorisé ? Doublons ? Thread‑safety Exemple
new ArrayList<>()
Oui Oui Oui Non
new ArrayList<>()
List.of(...)
Non Non Oui Oui*
List.of("a", "b")
Set.of(...)
Non Non Non Oui*
Set.of("a", "b")
Map.of(...)
Non Non Non Oui*
Map.of("a", 1, "b", 2)
Collections.unmodifiableList(...)
Non Dépend de l’originale Oui Non
Collections.unmodifiableList(list)

* — sécurité vis‑à‑vis des threads uniquement grâce à l’immuabilité : si personne ne modifie la collection, on peut la lire en toute sécurité depuis plusieurs threads.

8. Erreurs typiques avec List.of, Set.of, Map.of

Erreur n° 1 : tentative de modifier la collection.
Une erreur très fréquente consiste à tenter d’ajouter ou de supprimer un élément d’une collection créée via List.of, Set.of ou Map.of. Par exemple, names.add("David") ou ages.remove("Bob"). Cela mène toujours à une UnsupportedOperationException à l’exécution.

Erreur n° 2 : tentative d’ajouter null.
Si vous passez par erreur null à l’une des méthodes (par exemple List.of("Alice", null)), vous obtiendrez une NullPointerException. Les collections immuables de Java 9+ n’aiment pas null — et c’est, en réalité, une bonne chose.

Erreur n° 3 : doublons d’éléments dans Set.of ou Map.of.
Set.of("a", "b", "a") ou Map.of("x", 1, "x", 2) provoqueront une IllegalArgumentException. Par définition, un ensemble et une map ne peuvent pas contenir de doublons.

Erreur n° 4 : attendre une implémentation concrète.
À ne pas faire :

List<String> list = List.of("a", "b");
if (list instanceof ArrayList) { 
    // ...
} // C'est toujours false !

L’implémentation interne est cachée — ne vous fiez pas aux détails d’implémentation.

Erreur n° 5 : tenter d’utiliser des méthodes de modification.
Même des méthodes comme clear(), set(index, value) (pour une liste) lèveront des exceptions. Souvenez‑vous : les collections créées via les méthodes de fabrique sont immuables.

1
Mission
JAVA 25 SELF, niveau 34, leçon 0
Bloqué
Palette de couleurs stricte 🎨
Palette de couleurs stricte 🎨
1
Mission
JAVA 25 SELF, niveau 34, leçon 0
Bloqué
Protection des données : pas d'entrées vides ni de doublons ! 🛡️
Protection des données : pas d'entrées vides ni de doublons ! 🛡️
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION