Sie können tun, was Sie wollen, wenn Sie ein Iteratorobjekt verwenden, um die Elemente in Ihrem Satz zu durchlaufen. Sie können sie unterwegs entfernen und es ist in Ordnung. Wenn Sie sie jedoch in einer for-Schleife entfernen (entweder "Standard" für jede Art), werden Sie in Schwierigkeiten geraten:
Set<Integer> set = new TreeSet<Integer>();
set.add(1);
set.add(2);
set.add(3);
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()) {
Integer setElement = iterator.next();
if(setElement==2) {
iterator.remove();
}
}
for(Integer setElement:set) {
if(setElement==2) {
set.remove(setElement);
}
}
Nach dem Kommentar von @ mrgloom gibt es hier weitere Details, warum der oben beschriebene "schlechte" Weg ist, na ja ... schlecht:
Ohne auf zu viele Details darüber einzugehen, wie Java dies implementiert, können wir auf hoher Ebene sagen, dass der "schlechte" Weg schlecht ist, weil er in den Java-Dokumenten eindeutig als solcher festgelegt ist:
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
unter anderem festlegen, dass (Hervorhebung von mir):
" Beispielsweise ist es im Allgemeinen nicht zulässig, dass ein Thread eine Sammlung ändert, während ein anderer Thread darüber iteriert. Im Allgemeinen sind die Ergebnisse der Iteration unter diesen Umständen nicht definiert. Einige Iterator-Implementierungen (einschließlich derjenigen der gesamten Allzweck-Sammlung) Implementierungen, die von der JRE bereitgestellt werden) können diese Ausnahme auslösen, wenn dieses Verhalten erkannt wird "(...)
" Beachten Sie, dass diese Ausnahme nicht immer anzeigt, dass ein Objekt gleichzeitig von einem anderen Thread geändert wurde. Wenn ein einzelner Thread eine Folge von Methodenaufrufen ausgibt, die gegen den Vertrag eines Objekts verstoßen, kann das Objekt diese Ausnahme auslösen. Beispiel: if Ein Thread ändert eine Sammlung direkt, während er mit einem ausfallsicheren Iterator über die Sammlung iteriert. Der Iterator löst diese Ausnahme aus. "
Um näher auf Details einzugehen: Ein Objekt, das in einer forEach-Schleife verwendet werden kann, muss die Schnittstelle "java.lang.Iterable" ( hier javadoc ) implementieren . Dies erzeugt einen Iterator (über die in dieser Schnittstelle gefundene "Iterator" -Methode), der bei Bedarf instanziiert wird und intern einen Verweis auf das Iterable-Objekt enthält, aus dem er erstellt wurde. Wenn jedoch ein iterierbares Objekt in einer forEach-Schleife verwendet wird, ist die Instanz dieses Iterators für den Benutzer ausgeblendet (Sie können in keiner Weise selbst darauf zugreifen).
Dies, zusammen mit der Tatsache, dass ein Iterator ziemlich zustandsbehaftet ist, dh um seine Magie zu entfalten und kohärente Antworten für seine "next" - und "hasNext" -Methoden zu erhalten, muss das Hintergrundobjekt nicht durch etwas anderes als den Iterator selbst geändert werden Während der Iteration wird eine Ausnahme ausgelöst, sobald festgestellt wird, dass sich etwas im Hintergrundobjekt geändert hat, während es darüber iteriert.
Java nennt diese "Fail-Fast" -Iteration: dh es gibt einige Aktionen, normalerweise solche, die eine Iterable-Instanz ändern (während ein Iterator darüber iteriert). Der Teil "Fehler" des Begriffs "Fehler schnell" bezieht sich auf die Fähigkeit eines Iterators, zu erkennen, wann solche "Fehler" -Aktionen auftreten. Der "schnelle" Teil des "Fail-Fast" (und meiner Meinung nach "Best-Effort-Fast") beendet die Iteration über ConcurrentModificationException , sobald festgestellt werden kann, dass eine "Fail" -Aktion vorliegt geschehen.