Raccolta immutabile vs immodificabile


170

Dalla panoramica del framework delle collezioni :

Le raccolte che non supportano le operazioni di modifica (come add, removee clear) vengono definite non modificabili . Le raccolte che non sono modificabili sono modificabili .

Le raccolte che garantiscono inoltre che nessun cambiamento Collectionnell'oggetto sarà visibile sono indicate come immutabili . Le raccolte che non sono immutabili sono mutabili .

Non riesco a capire la distinzione.
Qual è la differenza tra non modificabile e immutabile qui?

Risposte:


207

Una raccolta non modificabile è spesso un wrapper attorno a una raccolta modificabile alla quale altri codici potrebbero ancora avere accesso . Pertanto, sebbene non sia possibile apportarvi modifiche se si ha solo un riferimento alla raccolta non modificabile, non è possibile fare affidamento sul fatto che i contenuti non cambiano.

Una collezione immutabile garantisce che nulla può più cambiare la collezione. Se racchiude una raccolta modificabile, si assicura che nessun altro codice abbia accesso a quella raccolta modificabile. Si noti che sebbene nessun codice possa modificare gli oggetti a cui la raccolta contiene riferimenti, gli oggetti stessi possono essere comunque mutabili - la creazione di una raccolta immutabile di StringBuildernon "in qualche modo" congela tali oggetti.

Fondamentalmente, la differenza è se un altro codice potrebbe essere in grado di cambiare la collezione dietro la schiena.


63
Una collezione immutabile non garantisce che nulla possa più cambiare. Fa semplicemente in modo che la collezione stessa non possa essere modificata (e non avvolgendola, ma copiandola). Gli oggetti presenti nella collezione possono ancora essere modificati e non viene data alcuna garanzia su quelli.
Hiery Nomus,

8
@HieryNomus: Nota che non ho detto che nulla poteva cambiare, ho detto che nulla poteva cambiare la collezione.
Jon Skeet,

1
ok, potrebbe averlo letto male;) Ma è bene chiarirlo però.
Hiery Nomus,

5
Quindi, quello che stai dicendo. Per la vera immutabilità hai bisogno di una collezione immutabile che contenga elementi di un tipo immutabile.
Evan Plaice,

1
@savanibharat: dipende dal fatto che ci sia ancora un percorso di codice che può ancora essere modificato list. Se qualcosa può chiamare in seguito, list.add(10)allora collrifletterà quel cambiamento, quindi no, non lo definirei immutabile.
Jon Skeet,

92

Fondamentalmente unModifiableCollection è una vista, quindi indirettamente potrebbe ancora essere "modificato" da qualche altro riferimento modificabile. Inoltre, poiché è solo una visualizzazione di sola lettura di un'altra raccolta, quando la raccolta di origine cambia la Raccolta non modificabile presenterà sempre i valori più recenti.

Tuttavia, la immutableraccolta può essere considerata come una copia di sola lettura di un'altra raccolta e non può essere modificata. In questo caso, quando la raccolta di origine cambia, la Raccolta immutabile non riflette le modifiche

Ecco una testcase per visualizzare questa differenza.

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

}

Produzione

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]
--

Non riesco a vedere alcuna differenza, puoi per favore sottolineare come Immutabile è diverso? Vedo che sia Immutabile che non modificabile generano errori e l'aggiunta non è supportata. Mi sto perdendo qualcosa qui?
AKS

2
@AKS Si prega di vedere l'uscita delle ultime tre voci della lista dopo l'aggiunta di 'c' alla lista mentre sia dimensioni modifiableListed unModifiableListè aumentata immutableListla dimensione non è cambiata
Prashant Bhate

1
Oh! fatto! :) .. Quindi qui hai modificato unmodifableList usando le modifiche in modifiableList, ma ImmutableList non può essere modificato. Ma allo stesso modo in cui è possibile modificare anche ImmutableList, penso che qui il client avrà accesso solo al riferimento ImmutableList, il riferimento a modifiableList, utilizzando il quale viene creato ImmutableList, non verrà esposto al client. giusto?
AKS

1
sì perché non è possibile fare riferimento a new ArrayList<String>(modifiableList)
immutableList

@PrashantBhate Ciao, non ci sono riferimenti a new ArrayList<String>(modifiableList)causa di new? Grazie.
Unheilig,

11

Penso che la differenza principale sia che il proprietario di una raccolta mutabile potrebbe voler fornire l'accesso alla raccolta ad un altro codice, ma fornire tale accesso tramite un'interfaccia che non consente all'altro codice di modificare la raccolta (riservando tale capacità al codice proprietario). Quindi la raccolta non è immutabile, ma alcuni utenti non sono autorizzati a modificare la raccolta.

Il tutorial di Oracle Collection Wrapper di Oracle ha questo da dire (enfasi aggiunta):

I wrapper non modificabili hanno due usi principali, come segue:

  • Per rendere immutabile una collezione una volta che è stata costruita. In questo caso, è buona norma non mantenere un riferimento alla collezione di supporto. Ciò garantisce assolutamente l'immutabilità.
  • Consentire a determinati clienti l'accesso in sola lettura alle strutture dei dati. Mantenete un riferimento alla collezione di supporto ma distribuite un riferimento al wrapper. In questo modo, i clienti possono guardare ma non modificare, mantenendo l'accesso completo .

3

Se stiamo parlando di JDK Unmodifiable*vs guava Immutable*, in realtà la differenza sta anche nelle prestazioni . Collezioni immutabili possono essere sia più veloce e più memoria efficiente se sono non wrapper intorno collezioni regolari (implementazioni del JDK sono wrapper). Citando la squadra di guava :

Il JDK fornisce i metodi Collections.unmodifiableXXX, ma a nostro avviso, questi possono essere

<...>

  • inefficiente: le strutture dati hanno ancora tutto il sovraccarico delle raccolte mutabili, inclusi controlli di modifica simultanei, spazio extra nelle tabelle hash, ecc.

pensando alle prestazioni, dovresti anche tener conto del fatto che un wrapper non modificabile non copia la raccolta, in quanto la versione immutabile usata in guava e ora anche in jdk9 + con ad esempio List.of(...)copia davvero due volte!
Benez,

2

Per citare i tutorial Java ™ :

A differenza dei wrapper di sincronizzazione, che aggiungono funzionalità alla raccolta di wrapping, i wrapper non modificabili eliminano la funzionalità. In particolare, eliminano la possibilità di modificare la raccolta intercettando tutte le operazioni che potrebbero modificare la raccolta e generando un UnsupportedOperationException . I wrapper non modificabili hanno due usi principali, come segue:

  • Per rendere immutabile una collezione una volta che è stata costruita. In questo caso, è buona norma non mantenere un riferimento alla collezione di supporto. Ciò garantisce assolutamente l'immutabilità.

  • Consentire a determinati clienti l'accesso in sola lettura alle strutture dei dati. Mantenete un riferimento alla collezione di supporto ma distribuite un riferimento al wrapper. In questo modo, i clienti possono guardare ma non modificare, mantenendo l'accesso completo.

(enfatizzare il mio)

Questo lo riassume davvero.


1

Come notato sopra, non modificabile non è come immutabile perché una raccolta non modificabile può essere modificata se, ad esempio, una raccolta non modificabile ha una raccolta delegata sottostante a cui fa riferimento un altro oggetto e che l'oggetto la modifica.

Per quanto riguarda l'immutabile, non è nemmeno ben definito. Tuttavia, generalmente significa che l'oggetto "non cambierà", ma che dovrebbe essere definito in modo ricorsivo. Ad esempio, posso definire immutabile su classi le cui variabili di istanza sono tutte primitive e i cui metodi non contengono argomenti e restituiscono primitive. I metodi quindi permettono ricorsivamente che le variabili di istanza siano immutabili e che tutti i metodi contengano argomenti immutabili e che restituiscono valori immutabili. I metodi dovrebbero essere garantiti per restituire lo stesso valore nel tempo.

Supponendo che possiamo farlo, esiste anche il concetto di thread sicuro. E potresti essere indotto a credere che l'immutabile (o non modificabile nel tempo) implichi anche la sicurezza dei thread. Tuttavia non è cosìe questo è il punto principale che sto sottolineando qui che non è stato ancora notato in altre risposte. Posso costruire un oggetto immutabile che restituisce sempre gli stessi risultati ma non è sicuro per i thread. Per vedere questo supponiamo che io costruisca una collezione immutabile mantenendo aggiunte e cancellazioni nel tempo. Ora la collezione immutabile restituisce i suoi elementi guardando la collezione interna (che potrebbe cambiare nel tempo) e quindi (internamente) aggiungendo ed eliminando gli elementi che sono stati aggiunti o eliminati dopo la creazione della collezione. Chiaramente, sebbene la raccolta restituisca sempre gli stessi elementi, non è thread-safe semplicemente perché non cambierà mai valore.

Ora possiamo definire immutabili come oggetti sicuri per i thread e che non cambieranno mai. Esistono linee guida per la creazione di classi immutabili che generalmente portano a tali classi, tuttavia, tenere presente che potrebbero esserci modi per creare classi immutabili, che richiedono attenzione alla sicurezza dei thread, ad esempio, come descritto nell'esempio di raccolta "istantanea" sopra.


1

I tutorial Java ™ dicono quanto segue:

A differenza dei wrapper di sincronizzazione, che aggiungono funzionalità alla raccolta di wrapping, i wrapper non modificabili eliminano la funzionalità. In particolare, eliminano la possibilità di modificare la raccolta intercettando tutte le operazioni che potrebbero modificare la raccolta e generando un UnsupportedOperationException. I wrapper non modificabili hanno due usi principali, come segue:

Per rendere immutabile una collezione una volta che è stata costruita. In questo caso, è buona norma non mantenere un riferimento alla collezione di supporto. Ciò garantisce assolutamente l'immutabilità.

Consentire a determinati clienti l'accesso in sola lettura alle strutture dei dati. Mantenete un riferimento alla collezione di supporto ma distribuite un riferimento al wrapper. In questo modo, i clienti possono guardare ma non modificare, mantenendo l'accesso completo.

Penso che sia una spiegazione abbastanza buona per capire la differenza.

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.