Unveränderliche vs nicht veränderbare Sammlung

170

Aus der Collections Framework-Übersicht :

Kollektionen, die keine Änderung (wie unterstützen add, removeund clear) , werden als nicht änderbar . Sammlungen, die nicht unveränderbar sind, können geändert werden .

Sammlungen, die zusätzlich garantieren, dass keine Änderung des CollectionObjekts sichtbar ist, werden als unveränderlich bezeichnet . Nicht unveränderliche Sammlungen sind veränderlich .

Ich kann den Unterschied nicht verstehen.
Was ist hier der Unterschied zwischen unveränderlich und unveränderlich ?

Cratylus
quelle

Antworten:

207

Eine nicht modifizierbare Sammlung ist häufig ein Wrapper um eine modifizierbare Sammlung, auf die anderer Code möglicherweise noch Zugriff hat . Obwohl Sie keine Änderungen daran vornehmen können, wenn Sie nur einen Verweis auf die nicht veränderbare Sammlung haben, können Sie sich nicht darauf verlassen, dass sich der Inhalt nicht ändert.

Eine unveränderliche Sammlung garantiert, dass nichts die Sammlung mehr ändern kann. Wenn eine veränderbare Sammlung umbrochen wird, wird sichergestellt, dass kein anderer Code Zugriff auf diese veränderbare Sammlung hat. Beachten Sie, dass, obwohl kein Code ändern kann, auf welche Objekte die Sammlung Verweise enthält, die Objekte selbst möglicherweise veränderlich sind - das Erstellen einer unveränderlichen Sammlung von StringBuilderObjekten "friert" diese Objekte nicht irgendwie ein.

Grundsätzlich besteht der Unterschied darin, ob anderer Code die Sammlung hinter Ihrem Rücken ändern kann.

Jon Skeet
quelle
63
Eine unveränderliche Sammlung garantiert nicht, dass sich nichts mehr ändern kann. Es wird lediglich sichergestellt, dass die Sammlung selbst nicht geändert werden kann (und zwar nicht durch Umbrechen, sondern durch Kopieren). Die in der Sammlung vorhandenen Objekte können weiterhin geändert werden, für die keine Garantie übernommen wird.
Hiery Nomus
8
@HieryNomus: Beachten Sie, dass ich nicht gesagt habe, dass sich nichts ändern kann - ich habe gesagt, dass nichts die Sammlung ändern kann.
Jon Skeet
1
ok, könnte das falsch verstanden haben;) Aber es ist gut, es zu klären.
Hiery Nomus
5
Also, was du sagst. Für echte Unveränderlichkeit benötigen Sie eine unveränderliche Sammlung, die Elemente eines unveränderlichen Typs enthält.
Evan Plaice
1
@savanibharat: Das hängt davon ab, ob es einen Codepfad gibt, der noch geändert werden kann list. Wenn etwas später anrufen kann list.add(10)dann colldiese Änderung reflektiert, also nein, würde ich es nicht unveränderlich nennen.
Jon Skeet
92

Grundsätzlich handelt es sich bei der unModifiableSammlung um eine Ansicht. Indirekt könnte sie also noch von einer anderen Referenz "modifiziert" werden, die geändert werden kann. Auch wenn es sich nur um eine schreibgeschützte Ansicht einer anderen Sammlung handelt, wird die nicht modifizierbare Sammlung immer mit den neuesten Werten angezeigt, wenn sich die Quellensammlung ändert.

Die immutableSammlung kann jedoch als schreibgeschützte Kopie einer anderen Sammlung behandelt und nicht geändert werden. In diesem Fall, wenn sich die Quellensammlung ändert, spiegelt die unveränderliche Sammlung die Änderungen nicht wider

Hier ist ein Testfall, um diesen Unterschied zu veranschaulichen.

@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());

}

Ausgabe

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
quelle
Ich kann keinen Unterschied feststellen. Können Sie bitte darauf hinweisen, wie unterschiedlich unveränderlich ist? Ich kann sehen, dass sowohl unveränderliche als auch nicht veränderbare Fehler auslösen und das Hinzufügen nicht unterstützt wird. Vermisse ich hier etwas?
AKS
2
@AKS Bitte sehen Sie die Ausgabe der letzten drei Listeneinträge nach dem Hinzufügen von 'c' zur Liste, während sich sowohl die Größe modifiableListals unModifiableListauch die immutableListGröße nicht geändert haben
Prashant Bhate
1
Oh! Ich habs! :) .. Hier haben Sie also unmodifableList mit den Änderungen in modizableList geändert, aber ImmutableList kann nicht geändert werden. Auf die gleiche Weise, wie Sie ImmutableList auch ändern können, wird der Client hier nur auf die ImmutableList-Referenz zugreifen. Der Verweis auf die modifizierbare Liste, mit der ImmutableList erstellt wird, wird dem Client nicht zugänglich gemacht. richtig?
AKS
1
Ja, weil es keinen Verweis auf new ArrayList<String>(modifiableList)unveränderliche Liste gibt. Kann nicht geändert werden
Prashant Bhate
@PrashantBhate Hallo, es gibt keinen Hinweis auf new ArrayList<String>(modifiableList)wegen new? Vielen Dank.
Unheilig
11

Ich denke, der Hauptunterschied besteht darin, dass der Eigentümer einer veränderlichen Sammlung möglicherweise Zugriff auf die Sammlung auf einen anderen Code gewähren möchte, diesen Zugriff jedoch über eine Schnittstelle bereitstellt, die es dem anderen Code nicht ermöglicht, die Sammlung zu ändern (während diese Funktion vorbehalten bleibt zum Besitzcode). Die Sammlung ist also nicht unveränderlich, aber bestimmte Benutzer dürfen die Sammlung nicht ändern.

Das Java Collection Wrapper-Tutorial von Oracle enthält Folgendes (Hervorhebung hinzugefügt):

Nicht modifizierbare Wrapper haben zwei Hauptverwendungen:

  • Eine Sammlung unveränderlich machen, sobald sie erstellt wurde. In diesem Fall empfiehlt es sich, keinen Verweis auf die Hintergrundsammlung beizubehalten. Dies garantiert absolut Unveränderlichkeit.
  • Damit bestimmte Clients schreibgeschützt auf Ihre Datenstrukturen zugreifen können. Sie behalten einen Verweis auf die Hintergrundsammlung bei, geben jedoch einen Verweis auf den Wrapper aus. Auf diese Weise können Clients suchen, aber nicht ändern, während Sie den vollständigen Zugriff behalten .
Michael Burr
quelle
3

Wenn wir über JDK Unmodifiable*gegen Guave sprechen Immutable*, liegt der Unterschied tatsächlich auch in der Leistung . Unveränderliche Sammlungen können sowohl schneller als auch speichereffizienter sein, wenn sie keine Wrapper für reguläre Sammlungen sind (JDK-Implementierungen sind Wrapper). Unter Berufung auf das Guaventeam :

Das JDK bietet Collections.unmodutableXXX-Methoden, aber unserer Meinung nach können dies sein

<...>

  • Ineffizient: Die Datenstrukturen haben immer noch den gesamten Aufwand für veränderbare Sammlungen, einschließlich gleichzeitiger Änderungsprüfungen, zusätzlichen Speicherplatz in Hash-Tabellen usw.
Dmide
quelle
Wenn Sie an die Leistung denken, sollten Sie auch berücksichtigen, dass ein nicht modifizierbarer Wrapper die Sammlung nicht kopiert, während die unveränderliche Version, die in Guave und jetzt auch in jdk9 + mit zB List.of(...)verwendet wird, tatsächlich zweimal kopiert!
Benez
2

So zitieren Sie die Java ™ -Tutorials :

Im Gegensatz zu Synchronisations-Wrappern, die der umschlossenen Sammlung Funktionen hinzufügen, nehmen die nicht modifizierbaren Wrapper die Funktionalität weg. Insbesondere nehmen sie die Möglichkeit zum Ändern der Sammlung weg, indem sie alle Vorgänge abfangen, die die Sammlung ändern würden, und eine UnsupportedOperationException auslösen . Nicht modifizierbare Wrapper haben zwei Hauptverwendungen:

  • Eine Sammlung unveränderlich machen, sobald sie erstellt wurde. In diesem Fall empfiehlt es sich, keinen Verweis auf die Hintergrundsammlung beizubehalten. Dies garantiert absolut Unveränderlichkeit.

  • Damit bestimmte Clients schreibgeschützt auf Ihre Datenstrukturen zugreifen können. Sie behalten einen Verweis auf die Hintergrundsammlung bei, geben jedoch einen Verweis auf den Wrapper aus. Auf diese Weise können Clients suchen, aber nicht ändern, während Sie den vollständigen Zugriff behalten.

(Hervorhebung von mir)

Das fasst es wirklich zusammen.

Bharath
quelle
1

Wie oben erwähnt, ist nicht modifizierbar nicht unveränderlich, da eine nicht modifizierbare Sammlung geändert werden kann, wenn beispielsweise einer nicht modifizierbaren Sammlung eine zugrunde liegende Delegatensammlung vorliegt, auf die von einem anderen Objekt verwiesen wird, und dieses Objekt sie ändert.

In Bezug auf unveränderliche ist es nicht einmal gut definiert. Im Allgemeinen bedeutet dies jedoch, dass sich das Objekt "nicht ändert", dies jedoch rekursiv definiert werden müsste. Zum Beispiel kann ich unveränderliche Klassen definieren, deren Instanzvariablen alle Grundelemente sind und deren Methoden alle keine Argumente enthalten und Grundelemente zurückgeben. Die Methoden ermöglichen dann rekursiv, dass die Instanzvariablen unveränderlich sind und alle Methoden Argumente enthalten, die unveränderlich sind und unveränderliche Werte zurückgeben. Es sollte garantiert werden, dass die Methoden im Laufe der Zeit denselben Wert zurückgeben.

Vorausgesetzt, wir können das, gibt es auch das Konzept Thread sicher. Und Sie könnten glauben gemacht werden, dass unveränderlich (oder nicht changeble im Laufe der Zeit) auch Thread-sicher impliziert. Dies ist jedoch nicht der Fallund das ist der Hauptpunkt, den ich hier mache, der in anderen Antworten noch nicht erwähnt wurde. Ich kann ein unveränderliches Objekt erstellen, das immer die gleichen Ergebnisse zurückgibt, jedoch nicht threadsicher ist. Um dies zu sehen, nehme ich an, dass ich eine unveränderliche Sammlung erstelle, indem ich im Laufe der Zeit Hinzufügungen und Löschungen behalte. Jetzt gibt die unveränderliche Sammlung ihre Elemente zurück, indem sie sich die interne Sammlung ansieht (die sich im Laufe der Zeit ändern kann) und dann (intern) die Elemente hinzufügt und löscht, die nach dem Erstellen der Sammlung hinzugefügt oder gelöscht wurden. Obwohl die Sammlung immer dieselben Elemente zurückgibt, ist sie natürlich nicht threadsicher, nur weil sie den Wert niemals ändert.

Jetzt können wir unveränderlich als Objekte definieren, die threadsicher sind und sich niemals ändern werden. Es gibt Richtlinien zum Erstellen unveränderlicher Klassen, die im Allgemeinen zu solchen Klassen führen. Beachten Sie jedoch, dass es Möglichkeiten gibt, unveränderliche Klassen zu erstellen, bei denen beispielsweise die Thread-Sicherheit berücksichtigt werden muss, wie im obigen Beispiel für die Sammlung "Snapshot" beschrieben.

dan b
quelle
1

In den Java ™ -Tutorials heißt es:

Im Gegensatz zu Synchronisations-Wrappern, die der umschlossenen Sammlung Funktionen hinzufügen, nehmen die nicht modifizierbaren Wrapper die Funktionalität weg. Insbesondere nehmen sie die Möglichkeit zum Ändern der Sammlung weg, indem sie alle Vorgänge abfangen, die die Sammlung ändern würden, und eine UnsupportedOperationException auslösen. Nicht modifizierbare Wrapper haben zwei Hauptverwendungen:

Eine Sammlung unveränderlich machen, sobald sie erstellt wurde. In diesem Fall empfiehlt es sich, keinen Verweis auf die Hintergrundsammlung beizubehalten. Dies garantiert absolut Unveränderlichkeit.

Damit bestimmte Clients schreibgeschützt auf Ihre Datenstrukturen zugreifen können. Sie behalten einen Verweis auf die Hintergrundsammlung bei, geben jedoch einen Verweis auf den Wrapper aus. Auf diese Weise können Clients suchen, aber nicht ändern, während Sie den vollständigen Zugriff behalten.

Ich denke, es ist eine gute Erklärung, um den Unterschied zu verstehen.

piyush121
quelle