2013-04-13 5 views
21

Sappiamo tutti che il modo più sicuro e "sicuro solo" di rimuovere un oggetto da una raccolta mentre lo itera è recuperare innanzitutto lo Iterator, eseguire un ciclo e rimuoverlo quando necessario;Come il metodo di rimozione di Iterator rimuove effettivamente un oggetto

Iterator iter=Collection.iterator(); 
while(iter.hasNext()){ 
    Object o=iter.next() 
    if(o.equals(what i'm looking for)){ 
     iter.remove(); 
    } 
} 

Quello che vorrei capire, e purtroppo non hanno trovato una spiegazione tecnica profonda circa, è come eseguire questa rimozione,
Se:

for(Object o:myCollection().getObjects()){ 
    if(o.equals(what i'm looking for)){ 
     myCollection.remove(o); 
    } 
} 

lancerà una ConcurrentModificationException, cosa fa "in termini tecnici" Iterator.remove() fare? Rimuove l'oggetto, interrompe il ciclo e riavvia il ciclo?

vedo nella documentazione ufficiale:

"rimuove l'elemento corrente Genera IllegalStateException se un tentativo è fatto per chiamare remove() che non è preceduta da una chiamata a successivo().."

La parte "rimuove l'elemento corrente", mi fa pensare la stessa identica situazione accadendo in un ciclo "regolare" => (eseguire test di uguaglianza e rimuovere, se necessario), ma perché è il ciclo Iterator ConcurrentModification- sicuro?

+0

si può vedere di persona: https://gist.github.com/kibotu/e480bd7505615a7311a6 –

risposta

14

Il modo in cui Iterator rimuove gli elementi dipende dalla sua implementazione, che potrebbe essere diversa per le diverse raccolte. Sicuramente non si rompe il ciclo si è in Ho appena guardato come ArrayList iteratore è implementata ed ecco il codice:.

public void remove() { 
    if (lastRet < 0) 
     throw new IllegalStateException(); 
    checkForComodification(); 

    try { 
     ArrayList.this.remove(lastRet); 
     cursor = lastRet; 
     lastRet = -1; 
     expectedModCount = modCount; 
    } catch (IndexOutOfBoundsException ex) { 
     throw new ConcurrentModificationException(); 
    } 
} 

Così verifica la presenza di modifiche simultanee, rimuove elemento utilizzando ArrayList pubblico rimuovere metodo e incrementa il contatore delle modifiche dell'elenco in modo che ConcurrentModificationException non venga gettato alla successiva iterazione.

+1

Che cos'è 'lastRet'? – m0skit0

+1

Indice dell'ultimo elemento restituito dall'iteratore. È impostato su -1 perché questo elemento è stato appena rimosso dall'elenco. –

+0

Il mio Java è un po 'arrugginito - ma qual è il 'ArrayList.this.remove (lastRet)'? Perché è necessario scrivere 'ArrayList.this'? È una classe interiore o qualcosa del genere? –

17

Il motivo per cui non è possibile modificare un elenco durante l'iterazione è perché l'iteratore deve sapere cosa restituire per hasNext() e next().

Come questo è fatto è specifica implementazione, ma si potrebbe avere uno sguardo al codice sorgente di ArrayList/AbstractList/ListaLinkata ecc

Si noti inoltre che in alcune situazioni è possibile utilizzare un codice come questo come alternativa :

List<Foo> copyList = new ArrayList<>(origList); 
for (Foo foo : copyList){ 
    if (condition){ 
    origList.remove(foo); 
    } 
} 

Ma questo codice sarà probabilmente eseguito leggermente più lento perché la raccolta deve essere copiato (copia superficiale solo) e l'elemento da rimuovere deve essere cercata.

Si noti inoltre che se si sta utilizzando direttamente l'iteratore si consiglia di utilizzare un ciclo for, invece di ciclo while in quanto ciò limita la portata della variabile:

for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){ 
... 
} 
Problemi correlati