Aufruf von remove in foreach-Schleife in Java [duplicate]

Lesezeit: 9 Minuten

Aufruf von remove in foreach Schleife in Java duplicate
Michael Bobick

Ist es in Java legal, remove für eine Sammlung aufzurufen, wenn die Sammlung mit einer foreach-Schleife durchlaufen wird? Zum Beispiel:

List<String> names = ....
for (String name : names) {
   // Do something
   names.remove(name).
}

Als Nachtrag, ist es legal, Elemente zu entfernen, die noch nicht iteriert wurden? Zum Beispiel,

//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
    // Do something
    while (names.remove(name));
}

  • Kein toller Plan. Nur weil die Sprache es bei jedem Lauf toleriert, ist es noch lange keine gute Idee.

    – Mark McKenna

    1. November 2011 um 12:57 Uhr

  • Sie müssen mich in schlechter Laune erwischt haben, aber mir scheint, die Antwort auf diese Frage kommt direkt aus der foreach-Dokumentation.

    – VorhangHund

    25. Juni 2012 um 11:47 Uhr

  • stackoverflow.com/questions/223918/…

    – Jeevi

    18. Oktober 2012 um 18:29 Uhr

  • Als Alternative könnten Sie erwägen, Ihre Sammlung nicht direkt zu ändern, sondern einen Filterkombinator wie Guava’s Iterables#filter verwenden: code.google.com/p/guava-libraries/wiki/FunctionalExplained Seien Sie sich seines faulen Verhaltens bewusst!

    – thSoft

    17. Dezember 2012 um 15:12 Uhr


  • Wollten Sie wirklich danach fragen? Collection speziell, eher als List die Sie in Ihrem Code verwenden? Falls Sie fragen wollten List und nicht Collection, dann bearbeiten Sie bitte diese Frage, um dies widerzuspiegeln – dann wäre diese Frage kein Duplikat! (Ein großer Unterschied von List vs Collection ist das List beinhaltet get in seiner Schnittstelle, während Collection nicht).

    – Zellpo

    19. Dezember 2018 um 22:49 Uhr


1647275232 272 Aufruf von remove in foreach Schleife in Java duplicate
Kennzeichen

Um sicher aus einer Sammlung zu entfernen, während Sie darüber iterieren, sollten Sie einen Iterator verwenden.

Zum Beispiel:

List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
   String s = i.next(); // must be called before you can call i.remove()
   // Do something
   i.remove();
}

Von dem Java-Dokumentation :

Die Iteratoren, die von den iterator- und listIterator-Methoden dieser Klasse zurückgegeben werden, sind ausfallsicher: Wenn die Liste zu irgendeinem Zeitpunkt nach der Erstellung des Iterators strukturell geändert wird, außer durch die eigenen remove- oder add-Methoden des Iterators, löst der Iterator eine ConcurrentModificationException aus. Somit versagt der Iterator angesichts gleichzeitiger Modifikationen schnell und sauber, anstatt willkürliches, nicht deterministisches Verhalten zu einem unbestimmten Zeitpunkt in der Zukunft zu riskieren.

Was vielen Anfängern vielleicht unklar ist, ist die Tatsache, dass das Iterieren über eine Liste mit den for/foreach-Konstrukten implizit einen Iterator erzeugt, auf den zwangsläufig nicht zugegriffen werden kann. Diese Informationen sind zu finden Hier

  • Beachten Sie, dass Sie i.next() aufrufen müssen, bevor Sie i.remove() aufrufen können: docs.oracle.com/javase/6/docs/api/java/util/Iterator.html

    – John Mellor

    7. März 2012 um 15:18 Uhr


  • Ich bin neugierig, warum gilt dies als sicher? Ist der Iterator als Mittelsmann fungieren?

    – Jakob P.

    12. Mai 2012 um 22:02 Uhr

  • Um das Javadoc für Iterator.remove() zu zitieren: “Das Verhalten eines Iterators ist nicht spezifiziert, wenn die zugrunde liegende Sammlung geändert wird, während die Iteration auf andere Weise als durch Aufrufen dieser Methode ausgeführt wird.” Der Iterator fungiert als Mittelsmann, um das Entfernen sicher durchzuführen, aber die Iteration wie erwartet fortsetzen zu lassen.

    – Kennzeichen

    25. Juni 2012 um 11:22 Uhr

  • Beachten Sie jedoch, dass die Methode remove () auf einem Iterator OPTIONAL ist und Ausnahmen auslöst, wenn sie nicht für Ihre spezielle Sammlung oder JVM implementiert ist

    Benutzer1743310

    12. Juni 2013 um 17:33 Uhr

  • @committedandroide Aus dem Javadoc für ConcurrentModificationException Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.

    – Kennzeichen

    22. März 2015 um 22:18 Uhr

Aufruf von remove in foreach Schleife in Java duplicate
Jared Oberhaus

Das willst du nicht. Dies kann je nach Sammlung zu undefiniertem Verhalten führen. Sie möchten eine verwenden Iterator direkt. Obwohl das for each-Konstrukt syntaktischer Zucker ist und wirklich einen Iterator verwendet, verbirgt es es vor Ihrem Code, sodass Sie nicht darauf zugreifen können, um es aufzurufen Iterator.remove.

Das Verhalten eines Iterators ist nicht spezifiziert, wenn die zugrunde liegende Auflistung geändert wird, während die Iteration auf andere Weise als durch Aufrufen dieser Methode ausgeführt wird.

Schreiben Sie stattdessen Ihren Code:

List<String> names = ....
Iterator<String> it = names.iterator();
while (it.hasNext()) {

    String name = it.next();
    // Do something
    it.remove();
}

Beachten Sie, dass der Code aufruft Iterator.removenicht List.remove.

Nachtrag:

Selbst wenn Sie ein Element entfernen, das noch nicht iteriert wurde, möchten Sie die Auflistung dennoch nicht ändern und dann die verwenden Iterator. Es könnte die Sammlung auf eine überraschende Weise ändern und zukünftige Operationen auf der beeinflussen Iterator.

  • Daumen hoch für den zusätzlichen Hinweis *dass der Code Iterator.remove aufruft, nicht List.remove”. Ich hätte fast verpasst und list.remove verwendet

    – pratpor

    12. September 2019 um 11:56 Uhr

Aufruf von remove in foreach Schleife in Java duplicate
ktamlyn

for (String name : new ArrayList<String>(names)) {
    // Do something
    names.remove(nameToRemove);
}

Sie klonen die Liste names und durchlaufen Sie den Klon, während Sie ihn aus der ursprünglichen Liste entfernen. Ein bisschen sauberer als die Top-Antwort.

  • das soll hier die erste antwort sein…

    – itzhar

    26. August 2015 um 10:45 Uhr

  • In acht nehmen. Bei zusammengesetzten Objekten ohne Gleichheit und hashCode-Methode schlägt dies möglicherweise einfach fehl. Die markierte Antwort entfernt sie jedoch sicher.

    – D3V

    28. Dezember 2015 um 14:54 Uhr

  • Obwohl kurz und sauber, wenn Leistung/Speichernutzung ein Problem sind, ist es erwähnenswert, dass diese Lösung in O(n²) ausgeführt wird und eine Kopie der ursprünglichen Liste erstellt, die Speicher und je nach Typ der Liste eine Operation erfordert . Wenn Sie einen Iterator über eine LinkedList verwenden, können Sie die Komplexität auf O (n) reduzieren.

    – SND

    15. Dezember 2017 um 7:53 Uhr

  • @SND Warum sollte es n ^ 2 sein? Das Erstellen einer Kopie der Liste ist O(n), daher ist dies auch O(n).

    – FINDarkside

    16. April 2019 um 17:43 Uhr

  • @FINDarkside das O (n ^ 2) stammt nicht vom Erstellen einer Kopie, sondern vom Schleifen durch die ArrayList (O (n)) während des Aufrufs von remove () für jedes Element (auch O (n)).

    – chakwok

    21. Mai 2019 um 18:40 Uhr

1647275233 839 Aufruf von remove in foreach Schleife in Java duplicate
Yishai

Das Java-Design der „erweiterten for-Schleife“ bestand darin, den Iterator nicht dem Code auszusetzen, aber die einzige Möglichkeit, ein Element sicher zu entfernen, besteht darin, auf den Iterator zuzugreifen. In diesem Fall müssen Sie es also auf die alte Schule tun:

 for(Iterator<String> i = names.iterator(); i.hasNext();) {
       String name = i.next();
       //Do Something
       i.remove();
 }

Wenn sich die erweiterte for-Schleife im echten Code wirklich lohnt, könnten Sie die Elemente einer temporären Sammlung hinzufügen und nach der Schleife removeAll in der Liste aufrufen.

BEARBEITEN (erneuter Nachtrag): Nein, das Ändern der Liste in irgendeiner Weise außerhalb der Methode iterator.remove() während der Iteration führt zu Problemen. Der einzige Weg, dies zu umgehen, besteht darin, eine CopyOnWriteArrayList zu verwenden, aber das ist wirklich für Parallelitätsprobleme gedacht.

Die billigste (in Bezug auf Codezeilen) Möglichkeit zum Entfernen von Duplikaten besteht darin, die Liste in ein LinkedHashSet (und dann bei Bedarf wieder in eine Liste) zu kopieren. Dadurch wird die Reihenfolge der Einfügungen beibehalten, während Duplikate entfernt werden.

1647275234 634 Aufruf von remove in foreach Schleife in Java duplicate
Serafim

Ich wusste nichts über Iteratoren, aber hier ist, was ich bis heute getan habe, um Elemente aus einer Liste innerhalb einer Schleife zu entfernen:

List<String> names = .... 
for (i=names.size()-1;i>=0;i--) {    
    // Do something    
    names.remove(i);
} 

Dies funktioniert immer und könnte in anderen Sprachen oder Strukturen verwendet werden, die Iteratoren nicht unterstützen.

  • Nur als Randnotiz, das sollte mit jeder Basisklassenliste gut funktionieren, wäre aber nicht auf esoterischere Strukturen übertragbar (wie zum Beispiel eine selbstsortierende Sequenz – im Allgemeinen irgendwo in der Ordnungszahl für einen bestimmten Eintrag könnte sich zwischen Listeniterationen ändern).

    – Mark McKenna

    1. November 2011 um 13:01 Uhr

  • Hinweis: Dies funktioniert genau deshalb, weil Sie rückwärts über die Elemente iterieren, sodass sich die Indizes der anderen verbleibenden Elemente nicht ändern, wenn Sie die entfernen ites Element. Wenn Sie von 0 bis schleifen würden size()-1, und beim Entfernen würden Sie die in anderen Antworten erwähnten Probleme sehen. Gute Arbeit, aber!

    – Jake Toronto

    24. September 2014 um 4:46 Uhr

  • Das ist einfach ein schrecklicher Weg. Abhängig von der Implementierung der Liste (oder des Satzes usw.) können so viele Dinge schief gehen.

    – DavidR

    29. November 2016 um 2:21 Uhr

  • Solche indizierten Operationen sollten nur an Listen durchgeführt werden, auf deren Elemente in O(1) konstanter Zeit zugegriffen werden kann. Wenn auf die Liste nicht wahlfrei zugegriffen werden kann (RandomAccess wird nicht implementiert), sollten Sie stattdessen einen Iterator verwenden, da diese Arten von Sammlungen normalerweise länger brauchen, um ein Element an einer bestimmten Indexposition abzurufen, z. B. eine LinkedList.

    – DerArchon

    4. Februar 2017 um 2:45 Uhr


  • Der Vorteil der obigen Methode ist, dass Sie (oder zumindest die meisten Leute) nicht nach “Java-Iterator-Beispiel” googeln müssen, sondern es sofort auswendig schreiben können.

    – Serafeim

    18. Oktober 2017 um 20:49 Uhr

Ja, Sie können die for-each-Schleife verwenden. Dazu müssen Sie eine separate Liste führen, um Elemente zu entfernen, und diese Liste dann mithilfe von aus der Namensliste entfernen removeAll() Methode,

List<String> names = ....

// introduce a separate list to hold removing items
List<String> toRemove= new ArrayList<String>();

for (String name : names) {
   // Do something: perform conditional checks
   toRemove.add(name);
}    
names.removeAll(toRemove);

// now names list holds expected values

  • Nur als Randnotiz, das sollte mit jeder Basisklassenliste gut funktionieren, wäre aber nicht auf esoterischere Strukturen übertragbar (wie zum Beispiel eine selbstsortierende Sequenz – im Allgemeinen irgendwo in der Ordnungszahl für einen bestimmten Eintrag könnte sich zwischen Listeniterationen ändern).

    – Mark McKenna

    1. November 2011 um 13:01 Uhr

  • Hinweis: Dies funktioniert genau deshalb, weil Sie rückwärts über die Elemente iterieren, sodass sich die Indizes der anderen verbleibenden Elemente nicht ändern, wenn Sie die entfernen ites Element. Wenn Sie von 0 bis schleifen würden size()-1, und beim Entfernen würden Sie die in anderen Antworten erwähnten Probleme sehen. Gute Arbeit, aber!

    – Jake Toronto

    24. September 2014 um 4:46 Uhr

  • Das ist einfach ein schrecklicher Weg. Abhängig von der Implementierung der Liste (oder des Satzes usw.) können so viele Dinge schief gehen.

    – DavidR

    29. November 2016 um 2:21 Uhr

  • Solche indizierten Operationen sollten nur an Listen durchgeführt werden, auf deren Elemente in O(1) konstanter Zeit zugegriffen werden kann. Wenn auf die Liste nicht wahlfrei zugegriffen werden kann (RandomAccess wird nicht implementiert), sollten Sie stattdessen einen Iterator verwenden, da diese Arten von Sammlungen normalerweise länger brauchen, um ein Element an einer bestimmten Indexposition abzurufen, z. B. eine LinkedList.

    – DerArchon

    4. Februar 2017 um 2:45 Uhr


  • Der Vorteil der obigen Methode ist, dass Sie (oder zumindest die meisten Leute) nicht nach “Java-Iterator-Beispiel” googeln müssen, sondern es sofort auswendig schreiben können.

    – Serafeim

    18. Oktober 2017 um 20:49 Uhr

1647275234 396 Aufruf von remove in foreach Schleife in Java duplicate
Vernunft

Diejenigen, die sagen, dass Sie ein Element nur über den Iterator sicher aus einer Sammlung entfernen können, sind nicht ganz richtig, Sie können dies sicher tun, indem Sie eine der gleichzeitigen Sammlungen wie ConcurrentHashMap verwenden.

  • Obwohl es vermeiden wird ConcurrentModificationExceptionkönnte es noch andere Indizierungsprobleme/Ausnahmen geben (mehr Details).

    – Zellpo

    20. Dezember 2018 um 1:22 Uhr

1002360cookie-checkAufruf von remove in foreach-Schleife in Java [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy