AFAIK, es gibt zwei Ansätze:
- Durchlaufen Sie eine Kopie der Sammlung
- Verwenden Sie den Iterator der tatsächlichen Sammlung
Zum Beispiel,
List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
// modify actual fooList
}
und
Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
// modify actual fooList using itr.remove()
}
Gibt es Gründe, einen Ansatz dem anderen vorzuziehen (z. B. den ersten Ansatz aus einfachen Gründen der Lesbarkeit zu bevorzugen)?
java
collections
iteration
user1329572
quelle
quelle
while
dass andere Regeln gelten alsfor
fooList
um eine Instanzvariable handelt und Sie während der Schleife eine Methode aufrufen, die schließlich eine andere Methode in derselben Klasse aufruftfooList.remove(obj)
. Habe das gesehen. In diesem Fall ist das Kopieren der Liste am sichersten.Antworten:
Lassen Sie mich einige Beispiele mit Alternativen nennen, um a zu vermeiden
ConcurrentModificationException
.Angenommen, wir haben die folgende Büchersammlung
Sammeln und entfernen
Die erste Technik besteht darin, alle Objekte zu sammeln, die wir löschen möchten (z. B. mithilfe einer erweiterten for-Schleife), und nach Abschluss der Iteration alle gefundenen Objekte zu entfernen.
Dies setzt voraus, dass der Vorgang, den Sie ausführen möchten, "Löschen" ist.
Wenn Sie diesen Ansatz "hinzufügen" möchten, funktioniert er auch. Ich gehe jedoch davon aus, dass Sie eine andere Sammlung durchlaufen, um zu bestimmen, welche Elemente Sie einer zweiten Sammlung hinzufügen möchten, und
addAll
am Ende eine Methode ausgeben .ListIterator verwenden
Wenn Sie mit Listen arbeiten, besteht eine andere Technik darin, eine zu verwenden,
ListIterator
die das Entfernen und Hinzufügen von Elementen während der Iteration selbst unterstützt.Auch hier habe ich im obigen Beispiel die Methode "Entfernen" verwendet, was Ihre Frage zu implizieren schien. Sie können jedoch auch die
add
Methode verwenden, um während der Iteration neue Elemente hinzuzufügen.Verwenden von JDK> = 8
Für diejenigen, die mit Java 8 oder höheren Versionen arbeiten, gibt es einige andere Techniken, die Sie verwenden können, um davon zu profitieren.
Sie können die neue
removeIf
Methode in derCollection
Basisklasse verwenden:Oder verwenden Sie die neue Stream-API:
In diesem letzten Fall weisen Sie zum Filtern von Elementen aus einer Sammlung den ursprünglichen Verweis der gefilterten Sammlung (dh
books = filtered
) neu zu oder verwenden die gefilterte SammlungremoveAll
den gefundenen Elementen aus der ursprünglichen Sammlung (dhbooks.removeAll(filtered)
).Verwenden Sie Unterliste oder Teilmenge
Es gibt auch andere Alternativen. Wenn die Liste sortiert ist und Sie aufeinanderfolgende Elemente entfernen möchten, können Sie eine Unterliste erstellen und diese dann löschen:
Da die Unterliste durch die ursprüngliche Liste unterstützt wird, wäre dies eine effiziente Möglichkeit, diese Unterkollektion von Elementen zu entfernen.
Ähnliches könnte mit sortierten Sätzen unter Verwendung der
NavigableSet.subSet
Methode oder einer der dort angebotenen Schneidemethoden erreicht werden.Überlegungen:
Welche Methode Sie verwenden, hängt möglicherweise davon ab, was Sie vorhaben
removeAl
Technik funktionieren mit jeder Sammlung (Sammlung, Liste, Satz usw.).ListIterator
Technik funktioniert offensichtlich nur mit Listen, vorausgesetzt, die angegebeneListIterator
Implementierung bietet Unterstützung für das Hinzufügen und Entfernen von Vorgängen.Iterator
Ansatz funktioniert mit jeder Art von Sammlung, unterstützt jedoch nur Entfernungsvorgänge.ListIterator
/Iterator
-Ansatz besteht der offensichtliche Vorteil darin, dass nichts kopiert werden muss, da wir es beim Iterieren entfernen. Das ist also sehr effizient.removeAll
Annähern besteht der Nachteil darin, dass wir zweimal iterieren müssen. Zuerst iterieren wir in der Foor-Schleife nach einem Objekt, das unseren Entfernungskriterien entspricht, und sobald wir es gefunden haben, bitten wir Sie, es aus der ursprünglichen Sammlung zu entfernen, was eine zweite Iterationsarbeit bedeuten würde, um nach diesem Element zu suchen entfernen Sie es.Iterator
Schnittstelle in Javadocs als "optional" markiert ist, was bedeutet, dass esIterator
Implementierungen geben kann, die ausgelöst werden,UnsupportedOperationException
wenn wir die Methode remove aufrufen. Daher würde ich sagen, dass dieser Ansatz weniger sicher ist als andere, wenn wir die Iterator-Unterstützung für das Entfernen von Elementen nicht garantieren können.quelle
removeAll(filtered)
. Eine Abkürzung dafür wäreremoveIf(b -> b.getIsbn().equals(other))
In Java 8 gibt es einen anderen Ansatz. Sammlung # removeIf
z.B:
quelle
Der erste Ansatz funktioniert, hat jedoch den offensichtlichen Aufwand, die Liste zu kopieren.
Der zweite Ansatz funktioniert nicht, da viele Container keine Änderungen während der Iteration zulassen. Dies beinhaltet
ArrayList
.Wenn die einzige Änderung das aktuelle Element zu entfernen, können Sie den zweiten Ansatz funktioniert durch die Verwendung
itr.remove()
(das heißt, verwenden Sie den Iterator ‚sremove()
Methode, nicht die Container ‘ s). Dies wäre meine bevorzugte Methode für Iteratoren, die dies unterstützenremove()
.quelle
Iterator
Schnittstelle in Javadocs als optional markiert ist, was bedeutet, dass es Iterator-Implementierungen geben kann, die möglicherweise ausgelöst werdenUnsupportedOperationException
. Daher würde ich sagen, dass dieser Ansatz weniger sicher ist als der erste. Abhängig von den Implementierungen, die verwendet werden sollen, könnte der erste Ansatz besser geeignet sein.remove()
auf der Originalsammlung selbst kann auch werfenUnsupportedOperationException
: docs.oracle.com/javase/7/docs/api/java/util/… . Die Java-Container-Schnittstellen sind leider als äußerst unzuverlässig definiert (ehrlich gesagt, wenn man den Punkt der Schnittstelle besiegt). Wenn Sie nicht genau wissen, welche Implementierung zur Laufzeit verwendet wird, ist es besser, die Dinge unveränderlich zu machen. Verwenden Sie beispielsweise die Java 8+ Streams-API, um die Elemente nach unten zu filtern und in einem neuen Container zu sammeln Ersetzen Sie das alte vollständig damit.Nur der zweite Ansatz wird funktionieren. Sie können die Sammlung nur während der Iteration ändern
iterator.remove()
. Alle anderen Versuche werden verursachenConcurrentModificationException
.quelle
Oldtimer Favorit (es funktioniert immer noch):
quelle
Sie können die zweite nicht ausführen, da selbst wenn Sie die
remove()
Methode für Iterator verwenden , eine Ausnahme ausgelöst wird .Persönlich würde ich die erste für alle
Collection
Instanzen bevorzugen , obwohlCollection
ich zusätzlich gehört habe, wie die neue erstellt wird , finde ich sie weniger fehleranfällig bei der Bearbeitung durch andere Entwickler. Bei einigen Collection-Implementierungen wird der Iteratorremove()
unterstützt, bei anderen nicht. Weitere Informationen finden Sie in den Dokumenten zu Iterator .Die dritte Alternative besteht darin, ein neues zu erstellen
Collection
, das Original zu durchlaufen und alle Mitglieder des erstenCollection
zum zweiten hinzuzufügenCollection
, die nicht gelöscht werden können. Abhängig von der Größe derCollection
und der Anzahl der Löschvorgänge kann dies im Vergleich zum ersten Ansatz erheblich Speicherplatz sparen.quelle
Ich würde die zweite wählen, da Sie keine Kopie des Speichers erstellen müssen und der Iterator schneller arbeitet. So sparen Sie Speicher und Zeit.
quelle
warum nicht das?
Und wenn es sich um eine Karte handelt, nicht um eine Liste, können Sie Keyset () verwenden.
quelle
get(i)
, alle Knoten besuchen müssen, bis Sie sie erreicheni
.Foo.remove(i);
du es tun sollsti--;
?