Entrambi i frammenti sono suddivisi in diversi modi!
Caso 1 (con Collection<String> col
):
Da un Collection
è indicizzata, l'unico metodo remove
sua interfaccia espone è Collection.remove(Object o)
, che rimuove l'oggetto uguale specificato. Fare col.remove(1);
prima chiama Integer.valueOf(1)
per ottenere un oggetto Integer
, quindi chiede l'elenco per rimuovere quell'oggetto. Poiché l'elenco non contiene alcun oggetto Integer
, non viene rimosso nulla. L'iterazione continua normalmente attraverso l'elenco e viene stampato abc
.
Caso 2 (con ArrayList<String> col
):
Quando col
tipo 's in fase di compilazione è ArrayList
, chiamando col.remove(1);
invece richiama il metodo ArrayList.remove(int index)
di rimuovere l'elemento nella posizione specificata, rimuovendo così b
.
Ora, perché non viene stampato c
? Per eseguire il loop su una raccolta con la sintassi for (X : Y)
, dietro le quinte chiama la raccolta per ottenere un oggetto Iterator
. Per il Iterator
restituito da un ArrayList
(e la maggior parte delle raccolte) non è sicuro eseguire modifiche strutturali all'elenco durante l'iterazione - a meno che non lo si modifichi con i metodi dello Iterator
stesso - perché lo Iterator
si confonderà e perderà traccia di quale elemento ritornare dopo. Ciò può comportare la ripetizione di elementi ripetuti, elementi saltati o altri errori. Ecco cosa succede qui: l'elemento c
è presente nell'elenco ma non è mai stato stampato perché hai confuso lo Iterator
.
Quando un Iterator
è in grado di rilevare questo problema è accaduto che ti avviserà lanciando un ConcurrentModificationException
. Tuttavia, il controllo che un Iterator
esegue per il problema è ottimizzato per la velocità, non è corretto al 100% e non sempre rileva il problema. Nel codice, se si modifica s.equals("b")
a s.equals("a")
o s.equals("c")
, viene generata un'eccezione (sebbene ciò potrebbe dipendere dalla particolare versione di Java). Dalle ArrayList
documentation:
Gli iteratori restituiti da iterator
e listIterator
i metodi di questa classe sono fail-fast: se la lista è strutturalmente modificato in qualsiasi momento dopo la creazione del iteratore, in alcun modo se non attraverso l'iteratore proprio Metodi remove
o add
, l'iteratore genererà un valore ConcurrentModificationException
. Quindi, di fronte a modifiche simultanee, l'iteratore fallisce rapidamente e in modo pulito, piuttosto che rischiare di comportarsi in modo arbitrario e non deterministico in un tempo indeterminato nel futuro.
Si noti che il comportamento fail-fast di un iteratore non può essere garantito in quanto è, in generale, impossibile fornire garanzie rigide in presenza di modifiche simultanee non sincronizzate. Gli iteratori fail-fast generano ConcurrentModificationException
in base al miglior sforzo.
per rimuovere gli elementi durante l'iterazione, è necessario modificare il for (X : Y)
in stile del ciclo in un ciclo manuale su un esplicito Iterator
, utilizzando il suo metodo remove
:
for (Iterator<String> it = col.iterator(); it.hasNext();) {
String s = it.next();
if (s.equals("b"))
it.remove();
System.out.print(s);
}
Questo è ora completamente sicuro . Ripeterà tutti gli elementi esattamente una volta (stampa abc
), mentre l'elemento b
verrà rimosso.
Se si desidera, è possibile ottenere lo stesso effetto senza un Iterator
utilizzare un int i
ciclo in stile, se si regola con attenzione l'indice dopo eventuali rimozioni:
for (int i = 0; i < col.size(); i++) {
String s = col.get(i);
if (s.equals("b")) {
col.remove(i);
i--;
}
System.out.print(s);
}
si può aggiungere la parte su come la 'Collezioni Il metodo .remove() 'accetta un' int' dovuto al boxing automatico? –
@TamoghnaChowdhury sure – Eran
Un'altra cosa da aggiungere ArrayList è la raccolta fail-fast, quindi se si intende rimuovere qualsiasi elemento dall'elenco durante l'attraversamento di usare CopyOnWriteArrayList, dato che è fail-safe – emkays