Collections immuables Java

115

À partir de la documentation de Java 1.6 Collection Framework :

Les collections qui ne prennent en charge aucune opération de modification (comme add, removeet clear) sont appelées non modifiables . [...] Les collections qui garantissent en outre qu'aucun changement dans l'objet Collection ne sera jamais visible sont appelées immuables .

Le deuxième critère me déroute un peu. Étant donné que la première collection n'est pas modifiable, et en supposant que la référence de collection d'origine a été supprimée, quels sont les changements auxquels il est fait référence dans la deuxième ligne? Se réfère-t-il aux changements des éléments contenus dans la collection, c'est-à-dire à l'état des éléments?

Deuxième question:
pour qu'une collection soit immuable, comment faire pour fournir les garanties supplémentaires spécifiées? Si l'état d'un élément de la collection est mis à jour par un thread, est-il suffisant pour l'immuabilité que ces mises à jour de l'état ne soient pas visibles sur le thread contenant la collection immuable?

Pour qu'une collection soit immuable, comment procède-t-on pour fournir les garanties supplémentaires spécifiées?

Bhaskar
la source
En général (en particulier dans les langages fonctionnels), la collection immuable (aka persistante) peut changer dans le sens que vous pouvez obtenir un nouvel état de cette collection, mais en même temps l' ancien état sera toujours disponible à partir d'autres liens. Par exemple, newCol = oldCol.add("element")produira une nouvelle collection qui est une copie de l'ancienne avec 1 élément supplémentaire, et toutes les références à la oldColpointeront toujours vers la même ancienne collection inchangée.
ffriend

Réponses:

154

Les collections non modifiables sont généralement des vues en lecture seule (wrappers) d'autres collections. Vous ne pouvez pas les ajouter, les supprimer ou les effacer, mais la collection sous-jacente peut changer.

Les collections immuables ne peuvent pas du tout être modifiées - elles n'enveloppent pas une autre collection - elles ont leurs propres éléments.

Voici une citation de goyave ImmutableList

Contrairement à Collections.unmodifiableList(java.util.List<? extends T>), qui est une vue d'une collection distincte qui peut encore changer, une instance de ImmutableListcontient ses propres données privées et ne changera jamais.

Donc, fondamentalement, pour extraire une collection immuable d'une collection mutable, vous devez copier ses éléments dans la nouvelle collection et interdire toutes les opérations.

Bozho
la source
@Bhaskar - voir mon dernier paragraphe.
Bozho
1
si la collection qui est enveloppée dans une autre collection non modifiable n'a pas d'autres références à celle-ci, le comportement de la collection non modifiable est-il exactement le même pour une collection immuable?
Bhaskar
1
@Bozho, Si la référence à la collection de sauvegarde n'est pas fournie et que seule une référence de wrapper de collection non modifiable est fournie, vous ne pouvez pas la changer. Alors pourquoi dites-vous "Vous ne pouvez pas être sûr"? Cela indique-t-il un scénario dans lequel un thread ou d'une manière ou d'une autre est modifié parce que la collection de sauvegarde est modifiable?
AKS
@AKS: Lorsqu'une collection est encapsulée unmodifiableList, le code qui ne reçoit qu'une référence à cette liste ne pourra pas la modifier, mais tout code qui avait une référence à la liste d'origine et pourrait la modifier avant la création de l'encapsuleur sera toujours pouvoir le faire par la suite. Si le code qui a créé la liste originale sait ce qui est arrivé à chaque référence qui y a jamais existé, et sait qu'aucune d'elles ne tombera entre les mains d'un code susceptible de modifier la liste, alors il peut savoir que la liste ne sera jamais modifié. Si la référence a été reçue d'un code extérieur, cependant ...
supercat
... il n'y a aucun moyen pour le unmodifiableList, ni pour aucun code qui l'utilise, de savoir si ou comment la collection encapsulée peut changer.
supercat du
86

La différence est que vous ne pouvez pas avoir de référence à une collection immuable qui autorise les modifications. Les collections non modifiables ne sont pas modifiables via cette référence , mais un autre objet peut pointer vers les mêmes données par lesquelles il peut être modifié.

par exemple

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);
Simon Nickerson
la source
1
quelles diverses causes, le cas échéant, peuvent apporter un changement dans une collection qui n'est pas modifiable, à condition que la référence de collection d'origine ne soit pas utilisée pour modifier la collection sous-jacente?
Bhaskar
21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1est mutable (c'est-à-dire ni non modifiable ni immuable ).
c2est non modifiable : il ne peut pas être changé lui - même, mais si plus tard je change c1alors que le changement sera visible dans c2.

C'est parce qu'il c2s'agit simplement d'un wrapper c1et pas vraiment d'une copie indépendante. Guava fournit l' ImmutableListinterface et certaines implémentations. Ceux-ci fonctionnent en créant une copie de l'entrée (à moins que l'entrée ne soit une collection immuable en elle-même).

Concernant votre deuxième question:

La mutabilité / immuabilité d'une collection ne dépend pas de la mutabilité / immuabilité des objets qu'elle contient. La modification d'un objet contenu dans une collection ne compte pas comme une "modification de la collection" pour cette description. Bien sûr, si vous avez besoin d'une collection immuable, vous souhaitez généralement qu'elle contienne des objets immuables.

Joachim Sauer
la source
2
@John: non, ce n'est pas le cas. Du moins pas selon la définition citée par l'OP.
Joachim Sauer
@JohnVint, vraiment? Je ne le penserais pas, car en utilisant c1, je peux toujours y ajouter des éléments, et cet ajout sera visible par c2. Cela ne veut-il pas dire que ce n'est pas immuable?
Bhaskar
Eh bien, je suppose que si c1 peut s'échapper, il ne serait pas immuable, mais l'immuabilité fait également référence au contenu de la collection. Je faisais plutôt référence à une collection non modifiable de java.util.Date's
John Vint
1
@Vint: "si c1n'échappe pas" n'est pas une considération qui se distingue n'importe où dans la spécification. À mon avis, si vous pouvez utiliser la collection d'une manière qui la rend non immuable, vous devriez toujours la considérer comme non immuable.
Joachim Sauer
1
Permettez-moi de citer la définition: "Des collections qui garantissent en plus ..." Collection.unmodifiableList() ne peut pas garantir cela, car il ne peut garantir que son argument n'échappe pas. Guava produit ImmutableList.of toujours un immuable List, même si vous laissez échapper ses arguments.
Joachim Sauer
17

Maintenant, java 9 a des méthodes d'usine pour Immutable List, Set, Map et Map.Entry.

Dans Java SE 8 et les versions antérieures, nous pouvons utiliser des méthodes utilitaires de classe Collections telles que unmodifiableXXX pour créer des objets Collection immuable.

Cependant, ces méthodes Collections.unmodifiableXXX sont une approche très fastidieuse et verbeuse. Pour surmonter ces lacunes, Oracle corp a ajouté quelques méthodes utilitaires aux interfaces List, Set et Map.

Maintenant dans java 9: ​​- Les interfaces List et Set ont des méthodes "of ()" pour créer une liste immuable ou des objets Set vide ou non vide comme indiqué ci-dessous:

Exemple de liste vide

List immutableList = List.of();

Exemple de liste non vide

List immutableList = List.of("one","two","three");
Opster Elasticsearch Pro-Vijay
la source
2
Et dans Java 10 List.copyOfet Set.copyOfont été ajoutés qui permettent de créer une copie non modifiable d'une liste / ensemble, ou de renvoyer la collection donnée si elle est déjà non modifiable, voir JDK-8191517
Marcono1234
6

Je crois que le point ici est que même si une collection est non modifiable, cela ne garantit pas qu'elle ne peut pas changer. Prenons par exemple une collection qui expulse des éléments s'ils sont trop anciens. Non modifiable signifie simplement que l'objet contenant la référence ne peut pas le changer, pas qu'il ne peut pas changer. Un vrai exemple de ceci est la Collections.unmodifiableListméthode. Il renvoie une vue non modifiable d'une liste. La référence List qui a été transmise à cette méthode est toujours modifiable et la liste peut donc être modifiée par n'importe quel détenteur de la référence qui a été transmise. Cela peut entraîner des exceptions ConcurrentModificationExceptions et d'autres problèmes.

Immuable, signifie qu'en aucun cas la collection ne peut être modifiée.

Deuxième question: une collection immuable ne signifie pas que les objets contenus dans la collection ne changeront pas, mais que cette collection ne changera pas dans le nombre et la composition des objets qu'elle contient. En d'autres termes, la liste des références de la collection ne changera pas. Cela ne signifie pas que les éléments internes de l'objet référencé ne peuvent pas changer.

John B
la source
Bon !! Donc, si je crée un wrapper sur une collection non modifiable et que je fais une référence de collection modifiable comme privée, je peux être sûr qu'elle est immuable. droite?
AKS
Si je comprends votre question, la réponse est non. L'encapsulation de l'Unmodifiable ne fait rien pour l'empêcher d'avoir la collection passée à unmodifiableListmodifiée. Si vous souhaitez faire cela, utilisez ImmutableList.
John B
0

Pure4J prend en charge ce que vous recherchez, de deux manières.

Premièrement, il fournit une @ImmutableValueannotation, de sorte que vous pouvez annoter une classe pour dire qu'elle est immuable. Il existe un plugin maven pour vous permettre de vérifier que votre code est réellement immuable (utilisation de finaletc.).

Deuxièmement, il fournit les collections persistantes de Clojure (avec des génériques ajoutés) et garantit que les éléments ajoutés aux collections sont immuables. La performance de ceux-ci est apparemment assez bonne. Les collections sont toutes immuables, mais implémentent des interfaces de collections Java (et des génériques) pour l'inspection. Mutation renvoie de nouvelles collections.

Avertissement: je suis le développeur de ce

Rob Moffat
la source