web-dev-qa-db-fra.com

Collections.unmodifiableList et copie défensive

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?

40
xdevel2000

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).

48
Joachim Sauer

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.

10
assylias

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

1
Carlo Pellegrini

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));
1
Travis Miller

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 :)

1
vikingsteve

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é.

1
A4L

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.

0
TheWhiteRabbit

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.

0
UR6LAD