Collection immuable vs non modifiable

170

Du cadre Collections Vue d' ensemble :

Collections qui ne prend pas en charge les opérations de modification ( par exemple add, removeet clear) sont appelés non modifiable . Les collections qui ne sont pas non modifiables sont modifiables .

Les collections qui garantissent en outre qu'aucune modification de l' Collectionobjet ne sera visible sont appelées immuables . Les collections qui ne sont pas immuables sont modifiables .

Je ne peux pas comprendre la distinction.
Quelle est la différence entre non modifiable et immuable ici?

Cratyle
la source

Réponses:

207

Une collection non modifiable est souvent un wrapper autour d'une collection modifiable à laquelle un autre code peut encore avoir accès . Ainsi, même si vous ne pouvez pas y apporter de modifications si vous n'avez qu'une référence à la collection non modifiable, vous ne pouvez pas vous fier à ce que le contenu ne change pas.

Une collection immuable garantit que rien ne peut plus changer la collection. S'il encapsule une collection modifiable, il s'assure qu'aucun autre code n'a accès à cette collection modifiable. Notez que bien qu'aucun code ne puisse changer les objets auxquels la collection contient des références, les objets eux-mêmes peuvent toujours être mutables - la création d'une collection immuable de StringBuilderne "gèle" pas d'une manière ou d'une autre ces objets.

Fondamentalement, la différence est de savoir si un autre code peut être en mesure de modifier la collection derrière votre dos.

Jon Skeet
la source
64
Une collection immuable ne garantit pas que rien ne peut plus changer. Il s'assure simplement que la collection elle-même ne peut pas être modifiée (et non par encapsulation, mais par copie). Les objets présents dans la collection peuvent encore être modifiés et aucune garantie n'est donnée sur ceux-ci.
Hiery Nomus
8
@HieryNomus: Notez que je n'ai pas dit que rien ne pouvait changer - j'ai dit que rien ne pouvait changer la collection.
Jon Skeet
1
ok, j'ai peut-être mal lu cela;) Mais il est bon de le clarifier.
Hiery Nomus
5
Alors, ce que vous dites. Pour une véritable immuabilité, vous avez besoin d'une collection immuable qui contient des éléments d'un type immuable.
Evan Plaice
1
@savanibharat: cela dépend s'il existe un chemin de code qui peut encore être modifié list. Si quelque chose peut appeler plus tard list.add(10)alors collreflétera ce changement, donc non, je ne l' appellerais pas immuable.
Jon Skeet
92

Fondamentalement, la unModifiablecollection est une vue, donc indirectement, elle pourrait encore être «modifiée» à partir d'une autre référence modifiable. Aussi comme il s'agit simplement d'une vue en lecture seule d'une autre collection, lorsque la collection source change, la collection non modifiable présentera toujours les dernières valeurs.

Cependant, immutableCollection peut être traitée comme une copie en lecture seule d'une autre collection et ne peut pas être modifiée. Dans ce cas, lorsque la collection source change, la collection immuable ne reflète pas les modifications

Voici un cas de test pour visualiser cette différence.

@Test
public void testList() {

    List<String> modifiableList = new ArrayList<String>();
    modifiableList.add("a");

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("--");


    //unModifiableList

    assertEquals(1, modifiableList.size());

    List<String> unModifiableList=Collections.unmodifiableList(
                                        modifiableList);

    modifiableList.add("b");

    boolean exceptionThrown=false;
    try {
        unModifiableList.add("b");
        fail("add supported for unModifiableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("unModifiableList.add() not supported");
    }
    assertTrue(exceptionThrown);

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);

    assertEquals(2, modifiableList.size());
    assertEquals(2, unModifiableList.size());
            System.out.println("--");



            //immutableList


    List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

    modifiableList.add("c");

    exceptionThrown=false;
    try {
        immutableList.add("c");
        fail("add supported for immutableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("immutableList.add() not supported");
    }
    assertTrue(exceptionThrown);


    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);
    System.out.println("immutableList:"+immutableList);
    System.out.println("--");

    assertEquals(3, modifiableList.size());
    assertEquals(3, unModifiableList.size());
    assertEquals(2, immutableList.size());

}

Production

modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--
Prashant Bhate
la source
Je ne peux voir aucune différence, pouvez-vous s'il vous plaît indiquer en quoi Immutable est différent? Je peux voir que les immuables et les non modifiables génèrent une erreur et que l'ajout n'est pas pris en charge. Est-ce que j'ai râté quelque chose?
AKS
2
@AKS Veuillez voir la sortie des trois dernières entrées de la liste après l'ajout de 'c' à la liste tandis que la taille de modifiableListet unModifiableLista augmenté la immutableListtaille n'a pas changé
Prashant Bhate
1
Oh! je l'ai! :) .. Vous avez donc ici modifié unmodifableList en utilisant les changements de modifiableList, mais ImmutableList ne peut pas être modifié. Mais de la même manière que vous pouvez modifier ImmutableList également, je pense qu'ici le client aura accès uniquement à la référence ImmutableList, la référence à modifiableList, à l'aide de laquelle ImmutableList est créée, ne sera pas exposée au client. droite?
AKS
1
oui car il n'y a pas de référence à new ArrayList<String>(modifiableList)immutableList ne peut pas être modifié
Prashant Bhate
@PrashantBhate Salut, il n'y a aucune référence à new ArrayList<String>(modifiableList)cause de new? Merci.
Unheilig
11

Je pense que la principale différence est que le propriétaire d'une collection mutable peut vouloir fournir l'accès à la collection à un autre code, mais fournir cet accès via une interface qui ne permet pas à l'autre code de modifier la collection (tout en réservant cette capacité au code propriétaire). La collection n'est donc pas immuable, mais certains utilisateurs ne sont pas autorisés à modifier la collection.

Le didacticiel Java Collection Wrapper d' Oracle a ceci à dire (soulignement ajouté):

Les wrappers non modifiables ont deux utilisations principales, comme suit:

  • Rendre une collection immuable une fois qu'elle a été construite. Dans ce cas, il est recommandé de ne pas conserver de référence à la collection de supports. Cela garantit absolument l'immuabilité.
  • Pour autoriser certains clients à accéder en lecture seule à vos structures de données. Vous conservez une référence à la collection de support, mais distribuez une référence à l'emballage. De cette façon, les clients peuvent regarder mais pas modifier, tout en conservant un accès complet .
Michael Burr
la source
3

Si nous parlons de JDK Unmodifiable*vs goyave Immutable*, la différence réside également dans les performances . Les collections immuables peuvent être à la fois plus rapides et plus efficaces en mémoire si elles ne sont pas des wrappers autour de collections régulières (les implémentations JDK sont des wrappers). Citant l'équipe de goyave :

Le JDK fournit des méthodes Collections.unmodifiableXXX, mais à notre avis, elles peuvent être

<...>

  • inefficace: les structures de données ont toujours toute la surcharge des collections mutables, y compris les contrôles de modification simultanés, l'espace supplémentaire dans les tables de hachage, etc.
Dmide
la source
en pensant aux performances, vous devez également prendre en compte le fait qu'un wrapper non modifiable ne copie pas la collection, alors que la version immuable utilisée dans goyave et maintenant aussi dans jdk9 + avec par exemple List.of(...)fait effectivement une copie deux fois!
benez
2

Pour citer les didacticiels Java ™ :

Contrairement aux wrappers de synchronisation, qui ajoutent des fonctionnalités à la collection encapsulée, les wrappers non modifiables enlèvent des fonctionnalités. En particulier, ils suppriment la possibilité de modifier la collection en interceptant toutes les opérations qui modifieraient la collection et en lançant une UnsupportedOperationException . Les wrappers non modifiables ont deux utilisations principales, comme suit:

  • Rendre une collection immuable une fois qu'elle a été construite. Dans ce cas, il est recommandé de ne pas conserver de référence à la collection de supports. Cela garantit absolument l'immuabilité.

  • Pour autoriser certains clients à accéder en lecture seule à vos structures de données. Vous conservez une référence à la collection de support, mais distribuez une référence à l'emballage. De cette façon, les clients peuvent regarder mais pas modifier, tout en conservant un accès complet.

(c'est moi qui souligne)

Cela résume vraiment les choses.

Bharat
la source
1

Comme indiqué ci-dessus, non modifiable n'est pas comme immuable car une collection non modifiable peut être modifiée si, par exemple, une collection non modifiable a une collection déléguée sous-jacente qui est référencée par un autre objet et que cet objet la modifie.

En ce qui concerne immuable, ce n'est même pas bien défini. Cependant, cela signifie généralement que l'objet "ne changera pas", mais cela devrait être défini de manière récursive. Par exemple, je peux définir immuable sur des classes dont les variables d'instance sont toutes des primitives et dont les méthodes ne contiennent toutes aucun argument et renvoient des primitives. Les méthodes permettent ensuite récursivement aux variables d'instance d'être immuables et à toutes les méthodes de contenir des arguments qui sont immuables et qui renvoient des valeurs immuables. Les méthodes doivent être garanties de renvoyer la même valeur au fil du temps.

En supposant que nous pouvons le faire, il existe également le concept thread safe. Et vous pourriez être amené à croire qu'immuable (ou non modifiable dans le temps) implique également la sécurité des threads. Cependant, ce n'est pas le caset c'est le principal point que je fais ici qui n'a pas encore été noté dans d'autres réponses. Je peux construire un objet immuable qui renvoie toujours les mêmes résultats mais qui n'est pas thread-safe. Pour voir cela, supposons que je construise une collection immuable en conservant les ajouts et les suppressions au fil du temps. Désormais, la collection immuable renvoie ses éléments en examinant la collection interne (qui peut évoluer au fil du temps), puis (en interne) en ajoutant et en supprimant les éléments qui ont été ajoutés ou supprimés après la création de la collection. Clairement, bien que la collection renvoie toujours les mêmes éléments, elle n'est pas thread-safe simplement parce qu'elle ne changera jamais de valeur.

Nous pouvons maintenant définir les objets immuables comme des objets thread-safe et qui ne changeront jamais. Il existe des directives pour créer des classes immuables qui mènent généralement à de telles classes, cependant, gardez à l'esprit qu'il peut y avoir des façons de créer des classes immuables, qui nécessitent une attention à la sécurité des threads, par exemple, comme décrit dans l'exemple de collection "snapshot" ci-dessus.

dan b
la source
1

Les didacticiels Java ™ disent ce qui suit:

Contrairement aux wrappers de synchronisation, qui ajoutent des fonctionnalités à la collection encapsulée, les wrappers non modifiables enlèvent des fonctionnalités. En particulier, ils suppriment la possibilité de modifier la collection en interceptant toutes les opérations qui modifieraient la collection et en lançant une UnsupportedOperationException. Les wrappers non modifiables ont deux utilisations principales, comme suit:

Rendre une collection immuable une fois qu'elle a été construite. Dans ce cas, il est recommandé de ne pas conserver de référence à la collection de supports. Cela garantit absolument l'immuabilité.

Pour autoriser certains clients à accéder en lecture seule à vos structures de données. Vous conservez une référence à la collection de support, mais distribuez une référence à l'emballage. De cette façon, les clients peuvent regarder mais pas modifier, tout en conservant un accès complet.

Je pense que c'est une assez bonne explication pour comprendre la différence.

piyush121
la source