Si j'écris
List<Integer> a1 = Arrays.asList(1, 2, 3);
List<Integer> a2 = Collections.unmodifiableList(a1);
a2
est en lecture seule mais si j'écris
a1.set(0,10);
alors a2
est également modifié.
Si dans l'API est dit:
Renvoie une vue non modifiable de la collection spécifiée. Cette méthode permet aux modules de fournir aux utilisateurs un accès "en lecture seule" à internal collections.
alors, pourquoi si je modifie la collection d'origine, la collection copiée par la cible est également modifiée?
Peut-être ai-je mal compris le sens et, le cas échéant, comment pouvons-nous écrire une copie défensive de cette collection?
Oui, vous l'avez bien compris. L'idée est que l'objet renvoyé par umodifiableCollection
ne peut pas être changé directement, mais peut changer par d'autres moyens (en modifiant directement la collection interne).
Tant que quelque chose a accès à la liste interne, la collection "non modifiable" peut être modifiée.
C'est pourquoi vous habituellement construisez une collection non modifiable et assurez-vous que rien ne peut jamais arriver à la liste interne:
Collection<Integer> myUmodifiableCollection = Collection.umodifiableCollection(Arrays.asList(1, 2, 3));
Étant donné que rien ne reçoit jamais une référence à la variable List
créée par asList
, il s'agit d'une collection véritablement non modifiable.
L'avantage de cette approche est qu'il n'est pas du tout nécessaire de copier la collection/liste d'origine, ce qui évite l'utilisation de la mémoire et de la puissance de calcul.
Guava fournit la ImmutableCollection
class (et ses sous-classes telles que ImmutableList
) qui fournissent de véritables collections immuables (généralement en copiant la source).
Peut-être ai-je mal compris le sens et si oui, comment écrire une copie défensive de cette collection?
Typiquement, vous l'utiliseriez de cette façon:
private List<Integer> a1 = Arrays.asList(1, 2, 3);
public List<Integer> getUnmodifiable() {
return Collections.unmodifiableList(a1);
}
Quelqu'un qui appelle getUnmodifiable
et n'a pas accès à l'intérieur de votre classe (c'est-à-dire qu'il ne peut pas accéder à la variable privée a1
) ne pourra pas modifier la liste renvoyée.
La déclaration:
Collections.unmodifiableList(a1);
retourne un wrapper sur la collection d'origine dont les méthodes de modificateur jettent UnsupportedOperationException
.
Le wrapper est en lecture seule, ce qui signifie que si vous modifiez a1
, les modifications sont répercutées sur la collection enveloppée a2
.
Si vous cherchiez à conserver a1
mutable et à en faire une copie immuable sans Guava, vous pouvez le faire.
List<Integer> a1 = Arrays.asList(1, 2, 3);
List<Integer> a2 = Collections.unmodifiableList(new ArrayList<>(a1));
L'idée est que vous ne pouvez pas modifier la liste via a2
.
La modification de la liste a1
modifiera effectivement ce que vous voyez dans a2
- ceci est prévu.
Il suffit de ne pas avoir un moyen public d'accéder à la liste a1
, et vous devriez avoir ce que vous voulez :)
l'API le dit collections internes dans votre cas, la collection n'est pas interne
le fait est que lorsque vous avez une liste privée en classe et un objet pour cette liste, vous pouvez vouloir que les appelants du getter ne puissent pas modifier la liste dans le cas où vous auriez à renvoyer une liste non modifiable. sinon, la liste renvoyée est simplement une référence à votre liste interne/privée et son contenu peut donc être modifié.
a1
et a2
référenceront les mêmes données (mémoire).
la partie non modifiable vient uniquement avec a2
en entrée.
imaging si vous passez a2
à une méthode où vous vous attendez à ce qu'elle soit idempotente. de tels cas a2
aide.
en résumé, vous ne pouvez pas modifier les données en utilisant le pointeur a2
.
Si vous avez besoin d'une liste non modifiable et immuable ou, en d'autres termes, d'une copie non modifiable de la liste source sans aucune dépendance par rapport à d'autres bibliothèques, essayez ceci:
Collections.unmodifiableList(Collections.list(Collections.enumeration(sourceList)))
Collections.list()
copie les valeurs de son énumération.