Raccolte immutabili Java


115

Dalla documentazione di Java 1.6 Collection Framework :

Collezioni che non supportano alcuna operazione di modifica (quali add, removee clear) sono indicati come immodificabile . [...] Le collezioni che garantiscono inoltre che nessun cambiamento nell'oggetto Collection sarà mai visibile sono indicate come immutabili .

Il secondo criterio mi confonde un po '. Dato che la prima raccolta non è modificabile e supponendo che il riferimento alla raccolta originale sia stato eliminato, quali sono le modifiche a cui si fa riferimento nella seconda riga? Si riferisce ai cambiamenti negli elementi contenuti nella collezione, cioè allo stato degli elementi?

Seconda domanda:
affinché una collezione sia immutabile, come si fa a fornire le garanzie aggiuntive specificate? Se lo stato di un elemento nella raccolta viene aggiornato da un thread, è sufficiente per l'immutabilità che quegli aggiornamenti nello stato non siano visibili sul thread che contiene la raccolta immutabile?

Affinché una collezione sia immutabile, come si fa a fornire le garanzie aggiuntive specificate?


In generale (specialmente nei linguaggi funzionali), la raccolta immutabile (ovvero persistente) può cambiare nel senso che è possibile ottenere un nuovo stato di questa raccolta, ma allo stesso tempo il vecchio stato sarà ancora disponibile da altri collegamenti. Ad esempio, newCol = oldCol.add("element")produrrà una nuova raccolta che è una copia di quella vecchia con 1 elemento in più e tutti i riferimenti a oldColpunteranno ancora alla stessa vecchia raccolta invariata.
ffriend

Risposte:


154

Le raccolte non modificabili sono in genere viste di sola lettura (wrapper) di altre raccolte. Non puoi aggiungerli, rimuoverli o cancellarli, ma la raccolta sottostante può cambiare.

Le raccolte immutabili non possono essere modificate affatto - non racchiudono un'altra raccolta - hanno i propri elementi.

Ecco una citazione da guava's ImmutableList

A differenza di Collections.unmodifiableList(java.util.List<? extends T>), che è una visualizzazione di una raccolta separata che può ancora cambiare, un'istanza di ImmutableListcontiene i propri dati privati ​​e non cambierà mai.

Quindi, in pratica, per ottenere una raccolta immutabile da una mutabile, è necessario copiare i suoi elementi nella nuova raccolta e non consentire tutte le operazioni.


@ Bhaskar - vedi il mio ultimo paragrafo.
Bozho

1
se la raccolta che è avvolta all'interno di un'altra raccolta immodificabile non ha altri riferimenti ad essa, allora il comportamento della raccolta immodificabile è esattamente lo stesso di una raccolta immutabile?
Bhaskar

1
@ Bozho, se non viene fornito il riferimento alla raccolta di backup e viene fornito solo il riferimento al wrapper della raccolta non modificabile, non è possibile modificarlo. Allora perché dici "Non puoi esserne sicuro"? Indica uno scenario in cui un thread o in qualche modo viene modificato perché la raccolta di backup è modificabile?
AKS

@AKS: Quando una raccolta viene racchiusa unmodifiableList, il codice che riceve solo un riferimento a tale elenco non sarà in grado di modificarlo, ma qualsiasi codice che aveva un riferimento all'elenco originale e potrebbe modificarlo prima che il wrapper fosse creato sarà comunque essere in grado di farlo in seguito. Se il codice che ha creato l'elenco originale sa cosa è successo a ogni riferimento ad esso mai esistito e sa che nessuno di essi cadrà nelle mani di codice che potrebbe modificare l'elenco, allora può sapere che l'elenco non sarà mai modificato. Se il riferimento è stato ricevuto da codice esterno, tuttavia ...
supercat

... non c'è modo per il unmodifiableList, né per qualsiasi codice che lo utilizza, di sapere se o come potrebbe cambiare la raccolta inserita.
supercat

86

La differenza è che non puoi avere un riferimento a una raccolta immutabile che consente modifiche. Le raccolte non modificabili non sono modificabili tramite quel riferimento , ma qualche altro oggetto può puntare agli stessi dati tramite i quali può essere modificato.

per esempio

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);

1
quali varie cause, se ce ne sono, possono portare un cambiamento in una raccolta che non è modificabile, a condizione che il riferimento alla raccolta originale non venga utilizzato per modificare la raccolta sottostante?
Bhaskar

21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1è mutabile (cioè né immodificabileimmutabile ). non
c2 è modificabile : non può essere modificato da solo, ma se in seguito cambio c1allora quel cambiamento sarà visibile in c2.

Questo perché c2è semplicemente un wrapper c1e non è davvero una copia indipendente. Guava fornisce l' ImmutableListinterfaccia e alcune implementazioni. Questi funzionano creando effettivamente una copia dell'input (a meno che l'input non sia una raccolta immutabile di per sé).

Per quanto riguarda la tua seconda domanda:

La mutabilità / immutabilità di una collezione non dipende dalla mutabilità / immutabilità degli oggetti in essa contenuti. La modifica di un oggetto contenuto in una raccolta non conta come una "modifica della raccolta" per questa descrizione. Ovviamente se hai bisogno di una raccolta immutabile, di solito vuoi che contenga anche oggetti immutabili.


2
@ John: no, non lo è. Almeno non secondo la definizione citata dal PO.
Joachim Sauer

@ JohnVint, davvero? Non la penserei così, perché usando c1, posso ancora aggiungere elementi al suo interno e questa aggiunta sarà visibile a c2. Questo non significa che non sia immutabile?
Bhaskar

Bene, immagino che se c1 può sfuggire, non sarebbe immutabile, ma l'immutabilità si riferisce anche al contenuto all'interno della raccolta. Mi riferivo più a una raccolta non modificabile di java.util.Date's
John Vint

1
@Vint: "if c1non scappa" non è una considerazione che si distingue da nessuna parte nella specifica. A mio parere, se si può usare la collezione in un modo che rende non immutabili, allora si dovrebbe sempre prendere in considerazione non immutabile.
Joachim Sauer

1
Vorrei citare la definizione: "Collezioni che garantiscono in più ..." (enfasi mia). Collection.unmodifiableList() non può garantirlo, perché non può garantire che il suo argomento non sfugga. Guava produce ImmutableList.of sempre un immutabile List, anche se lasci sfuggire i suoi argomenti.
Joachim Sauer

17

Ora java 9 ha metodi di fabbrica per Immutable List, Set, Map e Map.Entry.

In Java SE 8 e versioni precedenti, è possibile utilizzare metodi di utilità della classe Collections come unmodifiableXXX per creare oggetti Collection immutabili.

Tuttavia, questi metodi Collections.unmodifiableXXX sono un approccio molto noioso e dettagliato. Per superare queste carenze, Oracle corp ha aggiunto un paio di metodi di utilità alle interfacce List, Set e Map.

Ora in java 9: ​​- Le interfacce List e Set hanno metodi "of ()" per creare un elenco immutabile vuoto o non vuoto o oggetti Set come mostrato di seguito:

Esempio di elenco vuoto

List immutableList = List.of();

Esempio di elenco non vuoto

List immutableList = List.of("one","two","three");

2
E in Java 10 List.copyOfe Set.copyOfsono stati aggiunti che consentono di creare una copia non modificabile di un elenco / set, o restituire la raccolta data se è già non modificabile, vedere JDK-8191517
Marcono1234

6

Credo che il punto qui sia che anche se una collezione è immodificabile, ciò non garantisce che non possa cambiare. Prendiamo ad esempio una raccolta che elimina gli elementi se sono troppo vecchi. Non modificabile significa semplicemente che l'oggetto che contiene il riferimento non può cambiarlo, non che non può cambiare. Un vero esempio di questo è il Collections.unmodifiableListmetodo. Restituisce una vista non modificabile di un elenco. Il riferimento List passato a questo metodo è ancora modificabile e quindi l'elenco può essere modificato da qualsiasi detentore del riferimento passato. Ciò può causare ConcurrentModificationExceptions e altri problemi.

Immutabile, significa che in nessun modo la collezione può essere modificata.

Seconda domanda: una raccolta immutabile non significa che gli oggetti contenuti nella raccolta non cambieranno, ma solo che la raccolta non cambierà nel numero e nella composizione degli oggetti che contiene. In altre parole, l'elenco dei riferimenti della raccolta non cambierà. Ciò non significa che gli interni dell'oggetto a cui si fa riferimento non possano cambiare.


Buono !! Quindi, se creo un wrapper sulla raccolta non modificabile e creo un riferimento alla raccolta modificabile come privato, posso essere sicuro che è immutabile. giusto?
AKS

Se capisco la tua domanda, la risposta è no. Il wrapping dell'immodificabile non fa nulla per proteggerlo dal passaggio della raccolta a unmodifiableListmodifiche. Se vuoi fare questo usa ImmutableList.
John B

0

Pure4J supporta ciò che stai cercando , in due modi.

Innanzitutto, fornisce @ImmutableValueun'annotazione, in modo che tu possa annotare una classe per dire che è immutabile. C'è un plug-in Maven che ti consente di controllare che il tuo codice sia effettivamente immutabile (uso di finalecc.).

In secondo luogo, fornisce le raccolte persistenti di Clojure (con l'aggiunta di generici) e garantisce che gli elementi aggiunti alle raccolte siano immutabili. Le prestazioni di questi sono apparentemente piuttosto buone. Le raccolte sono tutte immutabili, ma implementano le interfacce (e generiche) delle raccolte Java per l'ispezione. La mutazione restituisce nuove raccolte.

Disclaimer: sono lo sviluppatore di questo

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.