Das Durchlaufen einer Sammlung, die Vermeidung ConcurrentModificationException beim entfernen von Objekten in einer Schleife
Wir alle wissen, kann man nicht tun:
for (Object i : l) {
if (condition(i)) {
l.remove(i);
}
}
ConcurrentModificationException
etc... dieses anscheinend funktioniert manchmal, aber nicht immer. Hier sind einige spezifische code:
public static void main(String[] args) {
Collection<Integer> l = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
l.add(4);
l.add(5);
l.add(6);
}
for (int i : l) {
if (i == 5) {
l.remove(i);
}
}
System.out.println(l);
}
Dies führt natürlich zu:
Exception in thread "main" java.util.ConcurrentModificationException
... obwohl mehrere threads tun es nicht... wie auch immer.
Was ist die beste Lösung für dieses problem? Wie kann ich Sie entfernen ein Element aus der Auflistung in einer Schleife, ohne daß diese Ausnahme?
Ich bin auch mit einem beliebigen Collection
hier, nicht unbedingt ein ArrayList
, so können Sie sich nicht darauf verlassen, dass get
.
Hinweis an den Leser: haben Sie eine Lesen des docs.oracle.com/javase/tutorial/collections/interfaces/..., es kann ein einfacher Weg, das zu erreichen, was Sie wollen zu tun.
InformationsquelleAutor Claudiu | 2008-10-21
Du musst angemeldet sein, um einen Kommentar abzugeben.
Iterator.remove()
ist sicher, Sie können es verwenden, wie diese:Beachten Sie, dass
Iterator.remove()
ist der einzige sichere Weg zum ändern einer collection während iteration; das Verhalten ist nicht spezifiziert, wenn die zugrunde liegende Sammlung ist geändert in sonstiger Weise während der iteration im Gange.Quelle: docs.oracle - > Die Collection-Schnittstelle
Ebenso, wenn Sie eine
ListIterator
und möchten hinzufügen Elemente, die Sie verwenden können,ListIterator#add
, aus dem gleichen Grund können SieIterator#remove
— es ist entworfen, um es zu ermöglichen.In Ihrem Fall, dass Sie versucht zu entfernen aus einer Liste, aber die gleiche Einschränkung gilt, wenn Sie versuchen zu
put
in eineMap
während der Iteration Ihre Inhalte.Sie haben zu verwenden .entfernen Sie in das iterator-und das ist nur in der Lage, entfernen Sie das aktuelle element, also Nein 🙂
Sich bewusst sein, dass dieser langsamer ist im Vergleich zu ConcurrentLinkedDeque oder CopyOnWriteArrayList (zumindest in meinem Fall)
ich verwendet, um die Iteration rückwärts über ein array und entfernen..
Ist es nicht möglich, die
iterator.next()
rufen Sie in der for-Schleife? Wenn nicht, kann jemand erklären, warum?InformationsquelleAutor Bill K
Dies funktioniert:
Habe ich angenommen, dass da eine foreach-Schleife ist syntaktischer Zucker für die Iteration, mit der ein iterator würde nicht helfen... aber es gibt Sie, diese
.remove()
Funktionalität.+1 für Beispiel-code zu verwenden iter.entfernen() in Zusammenhang, die Bill K ' s Antwort nicht [direkt] haben.
InformationsquelleAutor Claudiu
Mit Java 8 können Sie die neuen
removeIf
Methode. Angewendet auf dein Beispiel:Ist die Implementierung von equals() in diesem Fall empfiehlt sich auch?
übrigens
removeIf
verwendetIterator
undwhile
Schleife. Sie sehen es an java 8java.util.Collection.java
Einige Implementierungen wie
ArrayList
überschreiben Sie es aus performance-Gründen. Die, die Sie sich beziehen, ist nur die default-Implementierung.Nein,
equals
ist gar nicht hier, damit es nicht umgesetzt werden. (Aber natürlich, wenn Sieequals
im test dann muss es so umsetzen, wie Sie es wollen.)InformationsquelleAutor assylias
Da wurde die Frage schon beantwortet also der beste Weg ist, verwenden Sie die remove-Methode aus dem iterator-Objekt, ich gehe in die Besonderheiten des Platzes, wo der Fehler
"java.util.ConcurrentModificationException"
geworfen wird.Jede collection-Klasse hat einen privaten Klasse implementiert das Iterator interface und stellt Methoden wie
next()
,remove()
undhasNext()
.Den code für die nächste sieht etwas so aus...
Hier die Methode
checkForComodification
implementiertSo, wie Sie sehen können, wenn Sie explizit versuchen, entfernen Sie ein element aus der Auflistung. Es ergibt sich in
modCount
immer anders ausexpectedModCount
, was in der AusnahmeConcurrentModificationException
.InformationsquelleAutor Ashish
Können Sie entweder den iterator direkt wie du Sie erwähnt hast, sonst halten Sie eine zweite Sammlung, und fügen Sie jedes Element, das Sie entfernen möchten, um die neue Kollektion, dann removeAll am Ende. Dies ermöglicht Ihnen, halten mit der Typ-Sicherheit der for-each-Schleife, auf Kosten der erhöhten Speicher und cpu-Zeit (sollte nicht ein großes problem sein, es sei denn, Sie haben wirklich, wirklich großen Listen oder einem wirklich alten computer)
Fair genug, solange Sie nicht etwas anderes zu tun mit dem iterator - nachdem es ausgesetzt ist, macht es leichter, Dinge zu tun wie call .next() zweimal pro Schleife etc. Nicht ein riesiges problem, aber kann Probleme verursachen, wenn Sie tun etwas komplizierter, als nur durch eine Liste, um die Einträge zu löschen.
in der ursprünglichen Frage, Claudiu, entfernt aus der Sammlung, nicht den iterator.
Die Entfernung von der iterator entfernt von der zugrunde liegenden Auflistung... aber was ich sagte, im letzten Kommentar ist, dass, wenn Sie tun, etwas komplizierter als nur auf der Suche nach löscht in der Schleife (wie die korrekte Verarbeitung von Daten) über den iterator machen kann, einige Fehler leichter zu machen.
Wenn es ein einfacher Werte löschen, die nicht benötigt werden und die Schleife wird nur tun, eine Sache, mit dem iterator und direkt aufrufen .remove() ist absolut in Ordnung.
InformationsquelleAutor RodeoClown
In solchen Fällen ist eine gemeinsame trick ist (war?) um rückwärts zu gehen:
Sagte, ich bin mehr als glücklich, dass Sie haben bessere Möglichkeiten in Java 8, z.B.
removeIf
oderfilter
auf streams.Ja, das ist definitiv nur für
ArrayList
s oder ähnliche Sammlungen.Ich bin mit einer ArrayList, das funktionierte perfekt, danke.
die Indizes sind toll. Wenn es so üblich, warum nicht Sie
for(int i = l.size(); i-->0;) {
?InformationsquelleAutor Landei
Gleiche Antwort wie Claudius mit einer for-Schleife:
InformationsquelleAutor Antzi
Mit Eclipse-Sammlungen (ehemals GS Collections), die Methode
removeIf
definiert MutableCollection arbeiten:Mit Java 8 Lambda-syntax dies kann wie folgt geschrieben werden:
Den Aufruf
Predicates.cast()
ist hier notwendig, weil eine Standard -removeIf
Methode wurde Hinzugefügt, auf diejava.util.Collection
- Oberfläche in Java 8.Hinweis: ich bin committer für Eclipse-Sammlungen.
InformationsquelleAutor Donald Raab
Machen Sie eine Kopie der vorhandenen Liste und Durchlaufen neue Kopie.
Das hängt von der Größe der Liste und die Dichte der Objekte innerhalb. Immer noch eine wertvolle und gültige Lösung.
Ich habe mit dieser Methode. Es dauert ein wenig mehr Ressourcen, aber viel flexibler, klar.
InformationsquelleAutor Priyank Doshi
Mit einer herkömmlichen for-Schleife
FWIW - derselbe code würde immer noch funktionieren, nachdem modifiziert, um die Schrittweite
i++
in der loop-guard eher als innerhalb der Schleife.Korrektur ^: Das ist, wenn die
i++
Inkrementieren wurden nicht Bedingung - ich sehe jetzt, warum Sie es tun, in den Körper 🙂InformationsquelleAutor Lluis Felisart
Menschen sind, die Durchsetzung nicht entfernen aus einer Collection iteriert durch eine foreach-Schleife. Ich wollte nur darauf hinweisen, dass technisch falsch und genau beschreiben (ich weiß, die OP-Frage ist so weit Fortgeschritten, wie man dieses wissen) der code hinter dieser Annahme:
Es ist nicht, dass Sie nicht entfernen können von der iterierten
Colletion
eher, dass man nicht dann weiterhin iteration, wenn Sie tun. Daher derbreak
im code oben.Entschuldigung, wenn diese Antwort ist eine etwas spezialisierte use-case und mehr geeignet, um die original - thread ich hier angekommen aus, dass man als Duplikat gekennzeichnet (trotz diesem thread erscheinen differenziertere) dieser und gesperrt.
InformationsquelleAutor John
Einen
ListIterator
ermöglicht Ihnen das hinzufügen oder entfernen von Elementen in der Liste. Angenommen, Sie haben eine Liste vonCar
Objekte:previous
Methode.InformationsquelleAutor james.garriss
Ich habe einen Vorschlag für das problem oben. Keine Notwendigkeit von sekundären Liste oder eine zusätzliche Zeit. Finden Sie ein Beispiel, das würde die gleichen Sachen, aber in einer anderen Weise.
Dies würde vermeiden, dass die Parallelität Ausnahme.
ArrayList
und somit nicht darauf verlassen könnenget()
. Ansonsten wohl ein guter Ansatz, aber.(Klärung ^) OP ist über einen beliebigen
Collection
-Collection
- Schnittstelle nicht enthaltenget
. (Obwohl FWIWList
- Schnittstelle enthalten, 'get').Ich habe gerade eine separate, ausführlichere Antwort hier auch für
while
-Schleife eineList
. Aber +1 für diese Antwort, denn es kam zuerst.InformationsquelleAutor Nandhan Thiravia
ConcurrentHashMap oder ConcurrentLinkedQueue oder ConcurrentSkipListMap kann eine andere option, weil Sie nie ConcurrentModificationException werfen, auch wenn Sie entfernen oder hinzufügen.
java.util.concurrent
Paket. Einige andere ähnliche/common-use-case, Klassen aus diesem Paket sindCopyOnWriteArrayList
&CopyOnWriteArraySet
[aber nicht beschränkt auf jene, die].Tatsächlich habe ich gerade gelernt, dass, obwohl jene Daten Struktur-Objekte vermeiden
ConcurrentModificationException
sind, mit Ihnen in einer erweitert-for-Schleife kann immer noch bewirken, dass die Indizierung Probleme (ich.e: noch-skipping Elemente, oderIndexOutOfBoundsException
...)InformationsquelleAutor Yessy
InformationsquelleAutor jagdish khetre
Eine weitere Möglichkeit ist das erstellen einer Kopie der arrayList:
}
i
ist nicht einindex
sondern das Objekt. Vielleicht ruft esobj
würde besser passen.InformationsquelleAutor Nestor Milyaev
Im Falle ArrayList:remove(int index)- if(index letztes element position) es vermeidet, ohne
System.arraycopy()
und nimmt sich nicht die Zeit dafür.arraycopy Zeit erhöht wenn(index sinkt), durch die Art und Weise Elemente der Liste auch abnimmt!
die besten effektiven entfernen, Weg ist - das entfernen Ihrer Elemente in absteigender Reihenfolge:
while(list.size()>0)list.remove(list.size()-1);
//O(1)while(list.size()>0)list.remove(0);
//O(factorial(n))InformationsquelleAutor Nurlan
Der Haken ist, das nach dem entfernen des Elements aus der Liste überspringen, wenn Sie den internen iterator.next () - Aufruf. es funktioniert immer noch! Obwohl ich nicht vorschlagen, schreiben Sie code wie diesen hilft es zu verstehen, das Konzept dahinter 🙂
Prost!
InformationsquelleAutor Srinivasan Thoyyeti
Beispiel für thread-sichere collection Modifikation:
InformationsquelleAutor Yazon2006
Ich weiß, diese Frage ist zu alt, zu Java 8, aber für diejenigen, die mit Java 8 können Sie ganz einfach mit removeIf():
InformationsquelleAutor pedram bashiri
Ich weiß, diese Frage nimmt auch eine
Collection
, und nicht konkret alleList
. Aber für diejenigen, die Lesen diese Frage, die in der Tat die Arbeit mit einemList
Referenz, können Sie vermeidenConcurrentModificationException
mit einemwhile
-loop (beim ändern) statt, wenn Sie vermeiden möchtenIterator
(entweder, wenn Sie wollen, vermeiden es im Allgemeinen, oder vermeiden Sie es insbesondere, zu erreichen, eine Schleife, um verschiedene von start-zu-Ende-halt an jedes element [was ich glaube ist das einzige, umIterator
selbst tun können]):*Update: Siehe Kommentare unten, die Klärung der Analog ist auch erreichbar mit dem traditionellen-for-Schleife.
Keine ConcurrentModificationException aus, dass code.
Hier sehen wir den looping nicht am Anfang beginnen, und nicht aufhören, an jeder element ist (ich glaube
Iterator
selbst nicht tun können).FWIW wir sehen auch
get
aufgerufenlist
, die nicht getan werden konnte, wenn seine Referenz war nurCollection
(statt der spezifischenList
-TypCollection
) -List
Schnittstelle umfasstget
, aberCollection
- Schnittstelle nicht. Wenn nicht für diese Differenz, dann dielist
Referenz könnte stattdessen einCollection
[und daher technisch ist diese Antwort wäre dann eine direkte Antwort, stattdessen eine tangentiale Antwort].FWIWW gleiche code funktioniert immer noch nach modifiziert, um start zu Beginn an der Haltestelle bei der jedes element (genau wie
Iterator
Reihenfolge):Auch dies ist nur eine genauere Erklärung, diese Antwort stackoverflow.com/a/43441822/2308683
Gut zu wissen - vielen Dank! Dass die andere Antwort hat mir geholfen zu verstehen, dass es die erweitert-for-Schleife, die werfen würde
ConcurrentModificationException
, aber nicht der traditionellen-for-Schleife (die die andere Antwort verwendet) - nicht ahnend, dass, bevor Sie ist der Grund, warum ich motiviert war, zu schreiben diese Antwort (die ich fälschlicherweise dachte dann, dass es alle for-Schleifen, werfen würde die Ausnahme).InformationsquelleAutor cellepo
dies ist möglicherweise nicht der beste Weg, aber für die meisten kleinen Fälle sollte dies akzeptabel:
I don ' T erinnern, wo ich das Lesen von... für justiness ich werde dieses wiki, in der Hoffnung jemand findet es, oder einfach nur, um nicht zu verdienen rep ich nicht verdient.
InformationsquelleAutor
Lambda Ausdrücke und Collection-Methoden im Jdk 8 kommt im Handlichen und fügt einige syntaktische Zucker ?.
Die Methode
removeIf
Schleifen durch die Sammlung und Filter mit Prädikat. Ein Prädikat ist eine Funktion von einem argument, das einen booleschen Wert zurückzugeben...Wie
boolean _bool = (str) -> str.equals("text");
Dies verdoppelt die exakt gleiche Antwort hier von vor 4 Jahren von @ assylias (sieht aus wie Sie sogar kopiert Ihre Prädikat-Logik). Bitte nicht hinzufügen, duplizieren Unordnung wie diese (rep bekommen in andere Methoden, die legitim sind - ach, und Sieh einer an: Sie haben verloren rep aus dieser Antwort geht negative Stimmen..).
InformationsquelleAutor Barnabas Ukwuani