1. Einführung
In der Programmierung stoßen wir ständig auf Situationen, in denen aus einer großen Datenmenge nur die benötigten Elemente ausgewählt werden müssen – das ist das Filtern. Sie möchten nur gerade Zahlen behalten, Zeichenketten finden, die das Wort "Java" enthalten, oder Benutzer auswählen, die älter als 18 Jahre sind – all das sind Filteraufgaben.
In Java begegnen uns solche Operationen auf Schritt und Tritt, daher ist es wichtig, die verschiedenen Ausführungsarten sicher zu beherrschen und ihre Besonderheiten zu verstehen.
2. Filtern mit einer Schleife
Beginnen wir mit der grundlegendsten Methode – der gewöhnlichen for-Schleife. Dieser Ansatz wird imperativ genannt, weil Sie explizit angeben, was wie zu tun ist. Er ist gut lesbar und leicht zu verstehen.
Beispiel: nur gerade Zahlen beibehalten
import java.util.*;
public class FilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = new ArrayList<>(); // Wir erstellen eine neue Liste für das Ergebnis
for (Integer n : numbers) {
if (n % 2 == 0) { // Bedingung prüfen: gerade Zahl
evenNumbers.add(n);
}
}
System.out.println("Gerade Zahlen: " + evenNumbers);
}
}
Ergebnis:
Gerade Zahlen: [2, 4, 6, 8, 10]
Wichtig: Die ursprüngliche Collection (numbers) wird nicht verändert. Wir erstellen eine neue Ergebnisliste.
Beispiel: Filtern von Strings nach Teilzeichenfolge
List<String> words = Arrays.asList("java", "python", "javascript", "kotlin", "c++");
List<String> javaWords = new ArrayList<>();
for (String word : words) {
if (word.contains("java")) {
javaWords.add(word);
}
}
System.out.println(javaWords); // [java, javascript]
Gleiches Prinzip: Wir laufen die Liste der Strings durch und prüfen das Vorkommen der Teilzeichenfolge mit der Methode contains.
3. Elemente aus einer Collection entfernen: Warum ist das nicht trivial?
Wo liegt der Haken?
Manchmal möchte man löschen, was nicht benötigt wird, direkt aus der ursprünglichen Collection. Versucht man das jedoch während des Durchlaufens in einer for-each-Schleife, erhält man eine ConcurrentModificationException.
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
for (Integer n : numbers) {
if (n < 0) {
numbers.remove(n); // GEFÄHRLICH! ConcurrentModificationException!
}
}
Ergebnis:
Exception in thread "main" java.util.ConcurrentModificationException
Collections „mögen es nicht“, wenn sie während eines solchen Durchlaufs modifiziert werden – die Iteration bricht zusammen.
Wie löscht man Elemente korrekt aus einer Collection?
Methode 1: Iterator.remove() verwenden
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
Integer n = it.next();
if (n < 0) {
it.remove(); // Sicheres Entfernen!
}
}
System.out.println(numbers); // [1, 3, 5]
Hier erzeugen wir den Iterator mit iterator(), bewegen uns mit hasNext() und next() durch die Collection und entfernen unerwünschte Elemente mittels it.remove().
Methode 2: eine neue Liste nur mit den benötigten Elementen erstellen
List<Integer> numbers = Arrays.asList(1, -2, 3, -4, 5);
List<Integer> positive = new ArrayList<>();
for (Integer n : numbers) {
if (n >= 0) {
positive.add(n);
}
}
System.out.println(positive); // [1, 3, 5]
Wir fassen die ursprüngliche Collection nicht an und sammeln eine neue Liste. Der Ansatz ist sicher, anschaulich und lässt sich bei komplexeren Bedingungen leicht erweitern.
Methode 3: removeIf verwenden (Java 8+)
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [1, 3, 5]
In einer einzigen Zeile übergeben wir die Bedingung: „Lösche alles, was kleiner als null ist“. Die internen Details der sicheren Modifikation übernimmt die Collection.
Fazit: Zum Löschen von Elementen verwenden Sie keine for-each-Schleife. Wählen Sie zwischen Iterator.remove(), dem Erstellen einer neuen Liste oder dem knappen removeIf.
4. Wann welche Methode zum Filtern verwenden?
Der imperative Ansatz mit einer normalen Schleife ist praktisch, wenn Sie die Elemente nicht nur auswählen, sondern gleich etwas damit tun möchten (z. B. ausgeben oder transformieren). Er ist einfach und transparent.
Geht es konkret um das Löschen aus der ursprünglichen Collection, vermeiden Sie das Löschen in for-each. Verwenden Sie Iterator.remove() für schrittweise Kontrolle oder das moderne removeIf() – die kürzeste und ausdrucksstärkste Variante.
Beispiel: Es gibt eine Wortliste, es sollen alle Wörter mit weniger als vier Zeichen entfernt werden:
List<String> words = new ArrayList<>(Arrays.asList("Java", "is", "fun", "awesome", "code"));
words.removeIf(word -> word.length() < 4);
System.out.println(words); // [Java, awesome, code]
Die Methode removeIf akzeptiert ein Prädikat – eine Filterregel – und löscht alles, was ihm entspricht.
5. Typische Fehler beim Filtern von Collections
Fehler Nr. 1: Versuch, Elemente in einer for-each-Schleife aus der Collection zu löschen.
Dieser Code führt zu einer ConcurrentModificationException:
for (Integer n : numbers) {
if (n < 0) {
numbers.remove(n); // BUMM! ConcurrentModificationException
}
}
Korrekte Varianten: Verwenden Sie Iterator.remove() oder removeIf.
Fehler Nr. 2: falsch formulierte Filterbedingung.
Es sollen nur negative Zahlen entfernt werden:
List<Integer> numbers = new ArrayList<>(Arrays.asList(-3, -1, 0, 2, 4));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [0, 2, 4]
Wenn man jedoch fälschlicherweise „nichtpositive“ schreibt und <= verwendet, verschwindet auch die Null:
numbers.removeIf(n -> n <= 0); // Ergebnis: [2, 4] – die Null wurde versehentlich entfernt
Achten Sie auf die Genauigkeit der Bedingungen: Ein falscher Vergleich kann das Ergebnis vollständig verändern.
GO TO FULL VERSION