nullpointer.at

Java: Element aus List entfernen mit einer Schleife


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

Ähnliche Artikel:


Beitrag veröffentlicht

in

,

von

Kommentare

5 Antworten zu „Java: Element aus List entfernen mit einer Schleife“

  1. Avatar von Sebastian
    Sebastian

    Danke für das Tutorial. Aber die Zeile String a = iterator.next(); gibt bei mir den Fehler:
    Type mismatch: cannot convert from Object to
    String. Auch wenn ich das Objekt caste, wills nicht laufen….

    1. Avatar von loother
      loother

      wenn du den generischen Typ Iterator in der for-Schleife änderst, zu Iterator, funktioniert es ohne casten.

      1. Avatar von loother
        loother

        ok wurde durch den HTML-Parser ad absurdum geführt, neuer Versuch:

        Iterator, durch
        Iterator<String>

        ersetzen. (Für den Fall, dass es nicht geklappt hat: dem Iterator den Typ String in spitzen Klammern angeben)

        1. Avatar von Pummer Thomas

          Ich habe mir erlaubt dein Kommentar zu editieren, damit die spitzen Klammern korrekt angezeigt werden.

          Danke für deinen Input!

  2. Avatar von Englischlehrer
    Englischlehrer

    Kannst du bitte „middel“ in „middle“ ändern?
    Ist ja peinlich!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*