8 votes

Pourquoi pouvons-nous changer la liste non modifiable si nous avons la liste originale ?

En regardant le code de Collections j'ai appris que lorsqu'on utilise la méthode unmodifiableList(List list) ou unmodifiableCollection(Collection c) il ne s'agit pas de créer un nouvel objet mais de retourner la référence du même objet et de surcharger les méthodes qui peuvent modifier l'objet. List [ add , addall , remove , retainAll ... ]
J'ai donc effectué ce test :

List modifiableList = new ArrayList();
modifiableList.add ( 1 );   
List unmodifiableList = Collections.unmodifiableList( modifiableList );
// unmodifiableList.add(3);  // it will throw the exception 
modifiableList.add ( 2 );       
System.out.println( unmodifiableList );

Le résultat est [ 1,2 ] .
La question est de savoir pourquoi il s'agit du même objet. Pourquoi ne pas créer un nouvel objet ?

11voto

Bozho Points 273663

(réponse à la question en bas de page)

Lorsque vous créez une liste non modifiable, l'objectif est qu'elle ne soit pas modifiée. par d'autres personnes que vous - c'est-à-dire les clients d'une API.

la méthode unmodifiableList(..) crée un nouvel objet de type UnmodifiableList (mais ce n'est pas une classe publique), qui récupère la liste originale, et lui délègue toutes les méthodes sauf les méthodes qui la modifieraient.

Le fait est que, comme indiqué dans la documentation :

Renvoie une vue non modifiable de la liste spécifiée. Cette méthode permet aux modules de fournir aux utilisateurs un accès en "lecture seule" aux listes internes.

Donc, un exemple : Vous avez un List de dispositifs que votre API a détectés et peut faire fonctionner, et vous voulez leur donner un client de votre API. Mais il n'est pas censé changement les. Vous avez donc deux options :

  • donnez-lui une copie profonde de votre List de sorte que, même s'il la modifie, cela ne change rien. votre liste
  • donnez-lui une collection non modifiable - il ne peut pas la modifier, et vous lui épargnez la création d'une nouvelle collection.

Et voici maintenant la réponse au titre de votre question - la liste non modifiable est une voir de la collection originale. Ainsi, si vous devez y ajouter un nouvel élément - par exemple, si vous avez découvert un nouvel appareil qui vient d'être branché, les clients pourront le voir dans leur collection d'origine. vue non modifiable .

3voto

Raoul Duke Points 2091

Maintenant, la question est de savoir pourquoi il se réfère au même objet ? Pourquoi ne pas créer un nouvel objet ?

Performance. Il n'est tout simplement pas possible de faire une copie complète. Il faudrait un temps linéaire pour faire une copie complète, ce qui n'est évidemment pas pratique. De plus, comme d'autres l'ont déjà noté, le but est de pouvoir faire circuler la référence de la liste non modifiable sans avoir à se soucier qu'elle soit modifiée. Ceci est très utile pour les programmes multithreads.

2voto

m01 Points 1368

De la documentation :

public static List unmodifiableList(List list)

Renvoie un fichier non modifiable voir de la liste spécifiée. Cette méthode permet aux modules de fournir aux utilisateurs accès en "lecture seule". a interne listes. Les opérations de requête sur la liste renvoyée "lisent à travers" la liste spécifiée, et les tentatives de modification de la liste renvoyée, que ce soit directement ou via son itérateur, entraînent une UnsupportedOperationException.

1voto

Basil Bourque Points 8938

La réponse acceptée por Bozho est correcte. Voici un peu plus d'informations, un exemple de code, et une suggestion d'alternative.

L'unmodifiableList est soutenu par la liste originale

Ce unmodifiableList méthode dans Collections La classe utilitaire ne crée pas une nouvelle liste, mais une pseudo-liste. soutenu par par la liste originale. Toute tentative d'ajout ou de suppression effectuée par le biais de l'objet "non modifiable" sera bloquée, et le nom est donc conforme à son objectif. Mais en effet, comme vous l'avez montré, la liste originale peut être modifiée et affecte simultanément notre liste secondaire non-modifiable.

Ceci est expliqué dans la documentation de la classe :

Renvoie une vue non modifiable de la liste spécifiée. Cette méthode permet aux modules de fournir aux utilisateurs un accès en "lecture seule" aux listes internes. Les opérations de requête sur la liste renvoyée "lisent à travers" la liste spécifiée, et les tentatives de modification de la liste renvoyée, que ce soit directement ou via son itérateur, entraînent une UnsupportedOperationException.

Ce quatrième mot est la clé : view . Le nouvel objet liste n'est pas une nouvelle liste. Il s'agit d'une superposition. Tout comme papier calque ou film de transparence sur un dessin vous empêche de faire des marques sur le dessin, cela ne vous empêche pas d'aller en dessous pour modifier le dessin original.

La morale de l'histoire : N'utilisez pas Collections.unmodifiableList pour faire des copies défensives de listes.

Idem pour Collections.unmodifiableMap , Collections.unmodifiableSet et ainsi de suite.

Voici un autre exemple illustrant ce problème.

String dog = "dog";
String cat = "cat";
String bird = "bird";

List< String > originalList = new ArrayList<>( 3 );
originalList.add( dog );
originalList.add( cat );
originalList.add( bird );

List< String > unmodList = Collections.unmodifiableList( originalList );
System.out.println( "unmod before: " + unmodList );  // Yields [dog, cat, bird]
originalList.remove( cat );  // Removing element from original list affects the unmodifiable list?
System.out.println( "unmod after: " + unmodList );  // Yields [dog, bird]

Google Guava

Au lieu de la Collections pour une programmation défensive, je recommande d'utiliser la classe Google Guava et sa bibliothèque ImmutableCollections l'installation.

Vous pouvez faire une nouvelle liste.

public static final ImmutableList<String> ANIMALS = ImmutableList.of(
        dog,
        cat,
        bird );

Ou vous pouvez faire une copie défensive d'une liste existante. Dans ce cas, vous obtiendrez une nouvelle liste distincte. La suppression de la liste originale no affecter (rétrécir) la liste immuable.

ImmutableList<String> ANIMALS = ImmutableList.copyOf( originalList ); // defensive copy!

Mais n'oubliez pas que si la définition de la collection est distincte, les objets contenus sont partagés par la liste originale et la nouvelle liste immuable. En faisant cette copie défensive, nous ne dupliquons pas l'objet "chien". Un seul objet "chien" reste en mémoire, les deux listes contiennent une référence pointant vers le même chien. Si les propriétés de l'objet "chien" sont modifiées, les deux collections pointent vers le même objet "chien" et les deux collections verront donc la nouvelle valeur de la propriété du chien.

0voto

Paweł Dyda Points 10049

Je crois que le secret réside dans les détails d'implémentation... Collection.unmodifiableList() vous donnera simplement une liste modifiable décorée. Je veux dire que la liste non modifiable contient une référence à la liste modifiable en interne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X