2015-09-02 18 views
5

Desidero un List i cui elementi non possono essere rimossi né aggiunti. Ho pensato di aver trovato la risposta con Collections.unmodifiableList in Java 8. Ho passato la mia lista originale e recuperato una lista apparentemente non modificabile.Perché il mio `unmodifiableList` è modificabile?

Tuttavia, quando elimino un elemento dall'elenco originale, il mio elenco non modificabile viene modificato. Cosa sta succedendo?

Vedere questo codice demo. Il mio elenco non modificabile si riduce da 3 elementi 2 quando si elimina dall'originale.

String dog = "dog"; 
String cat = "cat"; 
String bird = "bird"; 

List<String> originalList = new ArrayList<>(3); 
originalList.add(dog); 
originalList.add(cat); 
originalList.add(bird); 

List<String> unmodList = Collections.unmodifiableList(originalList); 
System.out.println("unmod before: " + unmodList); // Yields [dog, cat, bird] 
originalList.remove(cat); // Removing element from original list affects the unmodifiable list? 
System.out.println("unmod after: " + unmodList); // Yields [dog, bird] 
+0

Mentre StackOverflow ha domande simili su Collections.unmodifiable ..., non sono riuscito a trovare nessuna risposta a questo problema in modo semplice e diretto. Così ho postato questa domanda e risposta. –

+2

Javadoc di 'Collections.unmodifiableList' è molto chiaro che solo la lista restituita non è modificabile. Ti aspettavi davvero che questa chiamata rendesse l'elenco originale non modificabile? – wero

+0

@wero "Il Javadoc ... è molto chiaro ..." - Mi permetto di dissentire. Il testo del doc, "Restituisce una vista non modificabile dell'elenco specificato." È tutt'altro che chiaro sulla raccolta originale che supporta il nuovo oggetto. Soprattutto non chiaro ai non addetti in quanto Java non ha una "visione" definita. Una rapida ricerca su Google rivelerà che non sono il solo a fare questa lettura errata. –

risposta

7

L'unmodifiableList è sostenuta da Lista originale

Che unmodifiableList metodo nella Collections classe di utilità non crea una nuova lista, si crea una pseudo-list sostenuta dalla lista originale. Qualsiasi tentativo di aggiunta o rimozione effettuato tramite l'oggetto "non modificabile" verrà bloccato, quindi il nome è all'altezza del suo scopo. Ma in effetti, come hai mostrato, la lista originale può essere modificata e contemporaneamente influenza la nostra lista secondaria non-non-modificabile.

Questo è scritto nella documentazione della classe:

Restituisce una vista immodificabile della lista specificata. Questo metodo consente ai moduli di fornire agli utenti accesso "di sola lettura" agli elenchi interni. Le operazioni di query sull'elenco restituito "read through" all'elenco specificato e i tentativi di modifica dell'elenco restituito, diretti o tramite il relativo iteratore, determinano un'eccezione UnsupportedOperationException.

Quella quarta parola è la chiave: view. Il nuovo oggetto elenco non è una nuova lista. È una sovrapposizione. Proprio come tracing paper o transparency film su un disegno si impedisce di fare segni sul disegno, non ti impedisce di andare sotto per modificare il disegno originale.

Morale della storia: non utilizzare Collections.unmodifiableList per creare copie difensive di elenchi.

Idem per Collections.unmodifiableMap, Collections.unmodifiableSet e così via.

Google Guava

Invece del Collections di classe, per la programmazione difensiva mi consiglia di utilizzare la libreria Google Guava e la sua struttura ImmutableCollections.

È possibile creare una nuova lista.

public static final ImmutableList<String> ANIMALS = ImmutableList.of(
     dog, 
     cat, 
     bird); 

Oppure è possibile effettuare una copia difensiva di un elenco esistente. In questo caso otterrai una nuova lista separata. L'eliminazione dall'elenco originale sarà non influenzerà (restringerà) l'elenco immutabile.

ImmutableList<String> ANIMALS = ImmutableList.copyOf(originalList); // defensive copy! 

Ma ricordate, mentre propria definizione della collezione è separata, gli oggetti contenuti sono condivisi sia dalla lista originale e nuova lista immutabile. Nel fare quella copia difensiva, non stiamo duplicando l'oggetto "cane". Solo un oggetto cane rimane in memoria, entrambi gli elenchi contengono un riferimento che punta allo stesso cane. Se le proprietà nell'oggetto "cane" vengono modificate, entrambe le raccolte puntano allo stesso oggetto cane singolo e quindi entrambe le raccolte vedranno il valore della proprietà fresca del cane.

+9

In realtà, la "morale della storia" è: non tenere mai un riferimento alla lista di accompagnamento. Usa la lista di supporto/Set/Mappa direttamente durante l'inizializzazione, quindi * wrap * con 'unmodifiableXxx()', e ** scarta ** il riferimento originale. --- E non modificabile non fa una copia difensiva. È una visione come hai già detto tu stesso. – Andreas

+0

Concordato con il commento precedente, la morale della storia è che si mantengono i riferimenti alla lista sottostante privata e non li si condivide ... – assylias

Problemi correlati