Suche
Für das Plus an Features

Java 9: Erweiterungen für das Stream-API

Michael Inden

© 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 als Predicate<T> übergebene Bedingung erfüllt ist.
  • dropWhile(Predicate<T>) – Überspringt Elemente des Streams, solange die
    als Predicate<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 einen Stream<T> mit einem Element, sofern das übergebene Element ungleich null ist. Ansonsten wird ein leerer Stream erzeugt.
  • iterate(T, Predicate<? super T>, UnaryOperator<T>) – Es wird ein Stream<T> mit dem als ersten Parameter übergebenen Startwert erzeugt. Die folgenden Werte werden durch den java.util.function.UnaryOperator berechnet. Im Gegensatz zu der bereits mit JDK 8 existierenden Methode iterate(T, UnaryOperator<T>) wird hierbei auch noch das übergebene java.util.function.Predicate<T> geprüft und die Erzeugung gestoppt, sobald dieses nicht mehr erfüllt ist.
Java 9 – Die Neuerungen

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.

Weitere Informationenen zum Buch gibt es hier!

Verwandte Themen:

Geschrieben von
Michael Inden
Michael Inden
Dipl.-Inform. Michael Inden ist Oracle-zertifizierter Java-Entwickler für JDK 6. Nach seinem Studium in Oldenburg war er lange Zeit als Softwareentwickler und -architekt bei verschiedenen internationalen Firmen tätig und arbeitet derzeit als Teamleiter Softwareentwicklung in Zürich. Michael Inden hat rund 20 Jahre Erfahrung beim Entwurf komplexer Softwaresysteme gesammelt, an diversen Fortbildungen und an mehreren Java-One-Konferenzen in San Francisco teilgenommen. Sein Wissen gibt er gerne als Trainer in Schulungen und auf Konferenzen weiter. Sein besonderes Interesse gilt dem Design qualitativ hochwertiger Applikationen mit ergonomischen, grafischen Oberflächen sowie dem Coaching von Kollegen.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.