Auf folgende Problemstellung stößt man doch immer wieder: Man muss ein Element aus einer Liste entfernen, welches bestimmten Kriterien entspricht. Jedoch kann man beim Löschen von Elementen aus einer Liste schnell in eine Falle tappen, indem man mit einfachen Schleifenkonstruktionen die Threadsicherheit aufs Spiel setzt. Wie möchte ich in folgenden Sourcen aufzeigen:
Es bietet sich, um die Elemente einer Liste zu überprüfen, die erweiterte for Schleife an. Sie ist gut zu lesen, einfach zu programmieren, man bekommt jedes einzelne Element als Referenz und muss nicht selbst auf den Index achten.
for (Element e : liste){ if (e.read().equals(“ja”) { ... }; }
Nutzt man dieses Konstrukt um ein Element der Liste ausfindig zu machen, unterwandert man beim Löschen die Integrität des Listen-Objekts und Java wirft eine ConcurrentModificationException. Dabei ist noch nichtmals mehr als ein Thread notwendig, wie im folgenden Beispiel gezeigt. Der Aufruf erscheint wie von einem anderen Thread. Das Element wird zwar gelöscht, jedoch beim Versuch auf das nächste Element zu wechseln tritt ein Fehler auf und das zweite Objekt das den Kriterien entsprochen hat wird nicht mehr aus der Liste entfernt.
import java.util.ArrayList; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.List; public class RemoveFromListTest { private List test = new ArrayList(); private void init() { test.add("start"); test.add("in"); test.add("the"); test.add("middel"); test.add("of"); test.add("the"); test.add("array"); test.add("end"); } private void printArray() { System.out.println(test); } private void remove(Object toRemove) { try { for (String a : test) { if (a.equals(toRemove)) { test.remove(a); } } } catch (ConcurrentModificationException e) { System.out.println("Exception when deleting \"" + toRemove.toString() + "\""); } } public static void main(String[] args) { RemoveFromListTest testList = new RemoveFromListTest(); testList.init(); testList.printArray(); testList.remove("in"); testList.printArray(); testList.remove("start"); testList.printArray(); testList.remove("end"); testList.printArray(); } }
Die Ausgabe:
[start, in, in, the, middel, of, the, array, end]
Exception when deleting „in“
[start, in, the, middel, of, the, array, end]
Exception when deleting „start“
[in, the, middel, of, the, array, end]
Exception when deleting „end“
[in, the, middel, of, the, array]
Erste Intention das zu Lösen ist es vielleicht sich die zu löschenden Objekte zu merken und später nach der Schleife aus der Liste zu nehmen. Die saubere Lösung für diese Fehlerquelle ist jedoch ein Iterator. Mit ihm können wir das Element direkt zum Zeitpunkt des Auffindens entfernen.
for (Iterator iterator = this.test.iterator(); iterator.hasNext();) { String a = iterator.next(); if (a.equals(toRemove)) { iterator.remove(); } }
Somit läuft man nicht mehr offen in eine Exception und muss sich die zu löschenden Objekte auch nirgends extra merken, was zu übersichtlicherem Code führt.
Na denn, schaut nach wie ihr sowas schon in alten Projekten gelöst habt! Könnt es auch gerne unten in die Kommentare stellen.
Greets!
Mehr Informationen
zum Iterator im Galileo Computing Open Book: Java ist auch eine Insel
Die ConcurrentModificationException beschrieben in der Java API
Schreibe einen Kommentar