Java 8: Iterable, Collection und List

von Hubert Schmid vom 2013-12-01

Letzte Woche habe ich einen Überblick über die neuen Methoden der Schnittstelle java.util.Map gegeben. Diese Woche setze ich den Ausblick auf Java 8 mit den Erweiterungen für Iterable, Collection und List fort. Dominiert werden sie von der sogenannten Stream-API, der ich aufgrund ihres Umfangs einen eigenen Artikel widmen werde. Doch auch neben ihr gibt es einige interessante Neuerungen, auf die ich im Folgenden eingehen werde.

Iterable::forEach

Die Schnittstelle java.lang.Iterable wurde mit Java 5 eingeführt und wird insbesondere für die For-Each-Loop verwendet. Neu ist nun die Methode forEach für die sogenannte interne Iteration. Ein Beispiel dafür ist im folgenden Listing zu sehen.

List<Employee> employees = ...; employees.forEach(e -> System.out.printf( "%-20s %7.0f €\n", e.getDisplayName(), e.getSalary()));

Diese Form ist nur unwesentlich kürzer als die entsprechende externe Interation mit einer For-Each-Loop. Selbst für sehr kompakte Ausdrücke wie im folgenden Beispiel ist die neue Methode nicht überzeugend.

String concat(List<String> tokens) { StringBuilder sb = new StringBuilder(); tokens.forEach(sb::append); return sb.toString(); }

Tatsächlich halte ich die sinnvollen Einsatzmöglichkeiten der forEach-Methode für überschaubar. Gute Beispiele kenne ich wenige. Eines davon ist im folgenden Listing zu sehen. Dort wird die forEach-Methode verwendet, um eine typische Vorbedingung zu prüfen. Im Unterschied zu den vorherigen Beispielen ist dabei die Reihenfolge der Ausführung nebensächlich, was möglicherweise ein gutes Entscheidungskriterium ist.

void doSomethingWith(List<Employee> employees) { employees.forEach(Objects::requireNonNull); // precondition // ... }

Collection::removeIf

Die Schnittstelle java.util.Collection wird in Java 8 um die Methode removeIf erweitert. Damit können aus einer Collection alle Elemente mit bestimmten Eigenschaften entfernt werden. Beispiele dafür sind im folgenden Listing zu sehen, und zur Übung kann man sich überlegen, wie der entsprechende Code ohne removeIf aussehen würde.

Collection<String> tokens = ...; tokens.removeIf(Objects::isNull); tokens.removeIf(String::isEmpty); tokens.removeIf(s -> s == null || s.isEmpty());

Die Methode removeIf vereinfacht diese Aufgabe ungemein. Die interessante Frage ist jedoch, wie häufig dieser Fall in der Praxis überhaupt auftritt. Ich kann mich zumindest nicht daran erinnern, diese Funktionalität in der Vergangenheit vermisst zu haben, was allerdings nichts zu bedeuten hat.

Collection::stream

Die stream-Methode liefert eine Instanz der Schnittstelle java.util.stream.Stream. Dabei handelt es sich um den Einstiegspunkt in die Stream-API. Wie bereits erwähnt gehe ich aufgrund des Umfangs an dieser Stelle nicht darauf ein. Nur so viel sei gesagt: Die Stream-API eignet sich primär für Abfragen und Auswertungen wohingegen die restlichen Methoden Collections verändern.

List::replaceAll

Die Methode replaceAll ist ähnlich zu removeIf. Jedes Element der Liste wird durch den Rückgabewert der übergebenen Operation ersetzt. Damit eignet sich die Methode insbesondere für unveränderliche Objekte. Das folgende Listing enthält einige Beispiele.

List<String> tokens = ...; tokens.replaceAll(String::trim); tokens.replaceAll(String::toUpperCase); tokens.replaceAll(String::intern); tokens.replaceAll("PREFIX-"::concat); tokens.replaceAll(s -> s.concat("-SUFFIX"));

Diese Beispiele lassen sich nicht einfach durch eine For-Each-Loop ersetzen. Mit Letzterer können zwar einfach alle Objekte geändert werden – hingegen besteht keine Möglichkeit die Referenzen innerhalb der Liste zu ersetzen.

All bedeutet natürlich nicht, dass tatsächlich alle Objekte ersetzt werden. Im Gegensatz zu removeIf ist das Filter-Prädikat jedoch unnötig, da die Bedingung in die Operation kodiert werden kann. Im folgenden Listing werden beispielsweise nur die null-Einträge durch einen Default-Wert ersetzt. Alle anderen Elemente bleiben unverändert.

List<Long> values = ...; values.replaceAll(v -> v == null ? 0 : v);

List::sort

Zuletzt noch ein paar Worte zu sort. Bisher konnte man Listen über die statischen Methoden der Klasse java.util.Collections sortieren. Java 8 erweitert nun auch die List-Schnittstelle um eine entsprechende Methode. Das folgende Listing enthält zwei einfache Beispiele. Dazu sollte man auch erwähnen, dass Java 8 zahlreiche Hilfsfunktionen für die Comparator-Schnittstelle bereitstellt, mit denen nun auch komplexere Sortierungen einfach zu formulieren sind.

List<String> tokens = ...; tokens.sort(String::compareTo); tokens.sort(String::compareToIgnoreCase);

Zurück zu sort: Es ist sicherlich nicht verkehrt, diese Methode in der List-Schnittstelle zu haben. Man sollte sich davon nur nicht zu viel versprechen. Denn weder auf die Wartbarkeit noch auf die Performance wirkt sich diese Methode nennenswert aus.