Java 9: Erweiterungen für das Stream-API

© Shutterstock.com / Profit_Image
Das kürzlich erschienene Java 9 besteht aus mehr als nur Modulen und dem Projekt Jigsaw. Über 90 sogenannte JEPs (JDK Enhancement Proposals) sind Teil von Version 9 des Java Development Kits. Im dritten Teil seiner Serie zu Java 9 stellt Michael Inden, Oracle-zertifizierter Java-Entwickler, einige mit JDK 9 eingeführte Erweiterungen des Stream-APIs vor, die nicht Teil eines JEPs sind.
Die vorliegende Artikelserie zu Java 9 setzt sich aus Texten von Michael Indens Buch „Java 9 – Die Neuerungen“ zusammen, das beim dpunkt.verlag erschienen ist.
PS: Verpassen Sie nicht unsere Infografik zu Project Jigsaw!
NNachdem wir im ersten Teil dieser Blog-Serie diverse kleinere Änderungen in der Syntax der Sprache Java kennengelernt haben, schauten wir uns im zweien Teil einige relevante Erweiterungen im JDK an, konkreter: im Process-API und den Collection-Factory-Methoden. Im dritten Teil soll es nun um die Ergänzungen im Stream-API gehen.
Erweiterungen im Stream-API
Das Stream-API mit dem Interface java.util.stream.Stream
stellt eine der wesentlichen Neuerungen in Java 8 dar. Streams besaßen bereits von Beginn an ein recht umfangreiches API. Dieses wurde mit Java 9 nochmals leicht erweitert.
Zunächst schauen wir uns folgende zwei neuen Methoden an:
takeWhile(Predicate<T>)
– Verarbeitet Elemente des Streams, solange die alsPredicate<T>
übergebene Bedingung erfüllt ist.dropWhile(Predicate<T>)
– Überspringt Elemente des Streams, solange die
alsPredicate<T>
übergebene Bedingung erfüllt ist.
Diese Ergänzungen findet man analog auch in den für die primitiven Typen spezialisierten Stream-Klassen IntStream
, LongStream
sowie DoubleStream
aus dem Package java.util.stream
. Dort ist dann jeweils das Prädikat auf den korrespondierenden Typ angepasst, etwa takeWhile(IntPredicate)
.
Beispiel für die Methoden takeWhile()
und dropWhile()
Zur Demonstration der Methode takeWhile()
wird ein unendlicher Stream von Ganzzahlen durch Aufruf von iterate()
auf einem IntStream
erzeugt, der mit der Zahl 1 beginnt. Für dropWhile()
nutzen wir einen Stream mit dem vordefinierten Wertebereich von 7 bis 14, den wir durch Aufruf von rangeClosed()
konstruieren. Zur Darstellung einer Besonderheit bei der Verarbeitung mit dropWhile()
verwenden wir schließlich einen Stream mit vordefinierten Werten, der durch einen Aufruf von of()
erzeugt wird:
public static void main(final String[] args) { // Unendliche Wertefolge erzeugen und alle bis 10 abgreifen final IntStream stream1 = IntStream.iterate(1, n -> n + 1); System.out.println("takeWhile: " + stream1.takeWhile(n -> n < 10). mapToObj(Integer::toString). collect(joining(", "))); // Wertebereich von 7 bis 14 erzeugen und alle kleiner 10 überspringen final IntStream stream2 = IntStream.rangeClosed(7, 14); System.out.println("dropWhile 1: " + stream2.dropWhile(n -> n < 10). mapToObj(Integer::toString). collect(joining(", "))); // Demonstration von dropWhile() bei gemischter Wertefolge final IntStream stream3 = IntStream.of(7,9,11,13,15,5,3,1); System.out.println("dropWhile 2: " + stream3.dropWhile(n -> n < 10). mapToObj(Integer::toString). collect(joining(", "))); }
Für alle Streams konvertieren wir durch den Aufruf von mapToObj(Integer::to-String)
die Zahlen in einen String und bereiten mit collect(joining(", "))
eine kommaseparierte Darstellung auf. Dabei stammt die Methode joining()
aus der Klasse java.util.stream.Collectors
und wurde zur besseren Lesbarkeit statisch importiert. Dann ist es leicht nachvollziehbar, dass es zu den folgenden Ausgaben kommt, wenn man das obige Programm startet:
takeWhile: 1, 2, 3, 4, 5, 6, 7, 8, 9 dropWhile 1: 10, 11, 12, 13, 14 dropWhile 2: 11, 13, 15, 5, 3, 1
Die Ausgabe der Zahlen hinter dem Text dropWhile 2
verdeutlicht, dass bei Aufrufen von dropWhile()
nur zu Beginn die Einhaltung der Bedingung überprüft wird. Gilt diese einmal, so erfolgt danach keine weitere Prüfung mehr und es werden im Anschluss möglicherweise Elemente konsumiert, die gegen die angegebene Bedingung verstoßen. Dieser Fall kann für takeWhile()
so nicht auftreten, da dort die Verarbeitung sofort abgebrochen würde.
Beide Methoden in Kombination
Auch in Kombination können die beiden Methoden sinnvoll eingesetzt werden. Das gilt etwa immer dann, wenn zunächst Informationen so lange aussortiert werden sollen, bis diese einem gewissen Gütekriterium oder Wert entsprechen, und dann im Anschluss so lange gelesen werden sollen, bis eine Abbruchbedingung erfüllt ist. Als Beispiel werden die Informationen, die zwischen den Markierungen <START>
und <END>
liegen, aus einen Stream<String>
extrahiert:
public static void main(final String[] args) { Stream<String> words = Stream.of("ab", "bla", "<START>", "Hier", "steht", "der", "Text", "zwischen", "den", "Start- ", "und", "Ende-Begrenzern", "<END>", "saas", "bla"); Stream<String> content = words.dropWhile(word -> !word.equals("<START>")) .skip(1) .takeWhile(word -> !word.equals("<END>")); content.forEach(System.out::println); }
Das skip(1)
ist nötig, um den Begrenzer <START>
nicht mit in die Ergebnisliste aufzunehmen. Die Ausgaben des obigen Programms zeigen sehr schön die Extraktion:
Hier steht der Text zwischen den Startund Ende-Begrenzern
Weitere neue Methoden
Neben den Neuerungen in Form der Methoden takeWhile()
und dropWhile()
findet man für Streams folgende neue Methoden:
ofNullable(T)
– Liefert einenStream<T>
mit einem Element, sofern das übergebene Element ungleichnull
ist. Ansonsten wird ein leerer Stream erzeugt.iterate(T, Predicate<? super T>, UnaryOperator<T>)
– Es wird einStream<T>
mit dem als ersten Parameter übergebenen Startwert erzeugt. Die folgenden Werte werden durch denjava.util.function.UnaryOperator
berechnet. Im Gegensatz zu der bereits mit JDK 8 existierenden Methodeiterate(T, UnaryOperator<T>)
wird hierbei auch noch das übergebenejava.util.function.Predicate<T>
geprüft und die Erzeugung gestoppt, sobald dieses nicht mehr erfüllt ist.

Michael Indens Buch bietet einen fundierten Einstieg in Java 9 sowie einen Überblick über die umfangreichen Neuerungen in der aktuellen Version. Damit eignet sich das Buch für all jene, die ihr Java-Wissen aktualisieren wollen. Dabei helfen eine Vielzahl an Übungen, um die einzelnen Themengebiete zu vertiefen und besser zu verstehen.
Neben Änderungen an der Sprache bilden auch die Erweiterungen in diversen APIs einen Schwerpunkt. Fehlen darf hier natürlich auch nicht ein Kapitel über die fundamentalste Änderung in Java 9: Project Jigsaw. Auch fortgeschrittenere Themen wie Services und die Migration bestehender Applikationen werden besprochen. Da Java 9 auch einige Auswirkungen auf Build Tools und IDEs mit sich bringt, gibt ein Kapitel einen Überblick über den derzeitigen Stand zum Tooling rund um die neue Java-Version.