2013-04-13 14 views
21

Wir alle wissen, dass die sicherste "und wahrscheinlich nur sichere" Weg ein Objekt aus einer Sammlung zu entfernen, während es iteriert, indem Sie zuerst die Iterator abrufen, führen Sie eine Schleife und entfernen Sie bei Bedarf;Wie Iterator entfernen Methode tatsächlich entfernen ein Objekt

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

Was ich verstehen möchte, und leider keine tiefe technische Erklärung, gefunden haben, ist, wie diese Entfernung durchgeführt wird,
If:

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

Wird ein ConcurrentModificationException werfen, was macht "in technischer Hinsicht" Iterator.remove()? Entfernt es das Objekt, unterbricht die Schleife und startet die Schleife neu?

Ich sehe in der offiziellen Dokumentation:

„das aktuelle Element Entfernt Werfe IllegalStateException wenn ein versucht wird, remove() zu nennen, die nicht durch einen Aufruf next() vorangestellt ist..“

Der Teil „entfernt das aktuelle Element“, lässt mich an der exakt gleichen Situation denken, in einer „normalen“ Schleife geschieht => (Gleichheitstest durchzuführen und entfernen, wenn nötig), aber warum ist die Iterator Schleife ConcurrentModification- sicher?

+0

Sie selbst sehen können: https://gist.github.com/kibotu/e480bd7505615a7311a6 –

Antwort

14

Wie genau Iterator Elemente entfernt, hängt von seiner Implementierung ab, die für verschiedene Collections unterschiedlich sein kann. Auf jeden Fall nicht die Schleife in dem Sie sich brechen Ich habe gerade sah, wie Arraylist Iterator implementiert ist und hier ist der Code:.

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

So ist es für die gleichzeitige Änderungen überprüft, entfernt Element öffentlichen Arraylist mit entfernen Methode und erhöht den Zähler für Listenänderungen, sodass ConcurrentModificationException bei der nächsten Iteration nicht ausgelöst wird.

+1

Was ist 'lastRet'? – m0skit0

+1

Index des letzten vom Iterator zurückgegebenen Elements. Es ist auf -1 gesetzt, weil dieses Element gerade aus der Liste entfernt wurde. –

+0

Mein Java ist ein bisschen eingerostet - aber was ist die 'ArrayList.this.remove (lastRet)'? Warum muss man 'ArrayList.this' schreiben? Ist es eine innere Klasse oder etwas? –

17

Der Grund, warum Sie eine Liste beim Iterieren nicht ändern können, ist, weil der Iterator wissen muss, was für hasNext() und next() zurückgegeben werden soll.

Wie dies geschieht, ist die Umsetzung spezifischer, aber man konnte einen Blick auf den Quellcode von Arraylist haben/AbstractList/LinkedList usw.

Beachten Sie auch, dass in einigen Situationen Sie einen Code wie diese als Alternative verwenden können :

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

Aber dieser Code wird wahrscheinlich etwas langsamer laufen, weil die Sammlung kopiert wird (flache Kopie nur) und das Element zu entfernen, durchsucht werden.

Beachten Sie auch, dass, wenn Sie den Iterator direkt verwenden es für Schleife empfohlen wird anstelle von while-Schleife ein zu verwenden, da dies den Umfang der Variablen begrenzt:

for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){ 
... 
} 
Verwandte Themen