Streams – Neue Methoden in Java 9

Wer schon einmal mit längeren Streams gearbeitet hat, kennt sicherlich die limit-Methode, welche auf einem Stream aufgerufen werden kann. Diese begrenzt die Menge an Tupeln, die weitergegeben werden, auf eine fixe Zahl. Leider war es bisher nicht möglich, diese Begrenzung dynamisch vorzunehmen und den Stream zu beenden, sobald eine bestimmte Bedingung nicht mehr erfüllt wird. Dafür und für einige andere Fälle schafft Java 9 nun mittels einiger neuer Methoden Abhilfe. Die neuen Methoden stammen aus dem JSR 379, und haben kein eigenes JEP erhalten.

Die takeWhile- und dropWhile-Methoden

Beginnen wir mit dem Fall der Stream-Limitierung via einer Bedingung. Dafür gibt es nun die takeWhile– und die dropWhile-Methoden.

Zunächst ein Beispiel für die takeWhile-Methode:

Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).takeWhile(x -> x < 6)
  .forEach(x -> System.out.print(x + " "));

Die Ausgabe sieht wie folgt aus:

1 2 3 4 5

Wie zu erwarten werden so lange Zahlen vom Stream genommen, bis die Bedingung (x < 6) nicht mehr erfüllt ist. Hierbei wird nicht der gesamte Stream durchlaufen, sondern der Durchlauf abgebrochen, sobald die Bedingung nicht mehr erfüllt ist. Das Verhalten ist vergleichbar mit dem einer for-Schleife. Das sieht man im folgenden Beispiel:

Stream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
  .takeWhile(x -> x < 6).forEach(x -> System.out.print(x + " "));

Die Ausgabe, ebenfalls:

1 2 3 4 5

Der Durchlauf wird wie gesagt abgebrochen, daher werden die Elemente weiter hinten im Stream nicht mehr ausgegeben, obwohl die Bedingung (x < 6) für sie wahr wäre. Wer die Bedingung auf alle Elemente im Stream angewendet haben möchte, verwendet stattdessen die filter-Methode.

Die dropWhile-Methode funktioniert analog zur takeWhile-Methode. Nur werden die Elemente hierbei so lange aus dem Stream geworfen, bis die Bedingung gilt. Danach wird die Bedingung nicht mehr geprüft.

IntStream.of(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1)
  .dropWhile(x -> x < 6).forEach(x -> System.out.print(x + " "));

Die Ausgabe lautet:

6 7 6 5 4 3 2 1

Die ersten fünf Elemente werden ignoriert, da für sie die Bedingung (x < 6) wahr ist. Für Element 6 gilt diese Bedingung nicht mehr, also bleiben alle Elemente ab Element 6 im Stream und werden ausgegeben.

Die iterate-Methode

Eine andere Methode, die ebenfalls mit Java 9 hinzugefügt wurde, ist die iterate-Methode. Hiermit kann ein Stream dynamisch (vergleichbar zu einer for-Schleife) erzeugt werden:

Stream.iterate(1, x -> x < 1000, x -> x * 2)
  .forEach(x -> System.out.print(x + " "));		

In diesem Beispiel werden etwa alle Zweierpotenzen unter 1000 ausgegeben. Die Ausgabe lautet (wie zu erwarten)
1 2 4 8 16 32 64 128 256 512

Die drei Argumente der iterate-Methode beschreiben dabei, analog zu denen einer for-Schleife, den initialen Wert (hier 1), die Abbruchbedingung (hier x < 1000) und den Ausdruck, der bei jedem Schleifendurchlauf ausgeführt werden soll (hier x * 2).

Wer einfach nur einen Zahlenstrom von n bis m haben möchte, dem sei im Übrigen zu der bereits vor Java 9 existierenden range(n, m)-Methode geraten.

Die ofNullable-Methode

Außerdem wurde noch die ofNullable-Methode hinzugefügt. Diese dient dazu, Nullchecks zu vermeiden, indem null-Elemente gar nicht erst im Stream landen.

In diesem Beispiel wird die count-Methode verwendet. Dazu die folgenden drei Codeblöcke:

String test = "Test";
long count = Stream.ofNullable(test).count();
System.out.printf("Count: %d\n", count);

test = null;
count = Stream.of(test).count();
System.out.printf("Count: %d\n", count);

count = Stream.ofNullable(test).count();
System.out.printf("Count: %d\n", count);

Im ersten Block wird ein Stream erstellt, der den String “Test” enthält. Anschließend wird die count-Methode auf dem Stream aufgerufen, die die Elemente zählen soll. Wie zu erwarten, gibt die Methode 1 zurück.

Vor dem zweiten Block wird die String-Variable test nun auf null gesetzt. Wenn nun Stream.of(test) (nicht Stream.ofNullable(test)) ausgeführt wird, wird ein Stream mit einem null-Element erstellt. Die count-Methode gibt 1 zurück.

Spannend ist der dritte Block. Hier wird die Variable test mit dem Nullwert über die ofNullable-Methode an den Stream übergeben. Nun gibt die count-Methode 0 zurück. Das liegt daran, dass die ofNullable-Methode gleich einen Nullcheck ausgeführt und das null-Element gar nicht erst in den Stream eingefügt hat. Dadurch ist der Stream leer und die count-Methode liefert 0 zurück.

Hier noch einmal die gesamte Ausgabe der drei Blöcke:

Count: 1
Count: 1
Count: 0

Das ist sehr praktisch. Nun sind in allen weiteren Methoden, die auf dem Stream aufgerufen werden, keine Nullchecks mehr erforderlich.

Zusammenfassung

Mit Java 9 sind die folgenden neuen Methoden hinzugekommen:

  • takeWhile(Predicate<? super T), um einen Stream zu terminieren, sobald die Bedingung nicht mehr wahr ist
  • dropWhile(Predicate<? super T), um so lange die Elemente im Stream zu ignorieren, wie die Bedingung wahr ist
  • iterate(T, Predicate, UnaryOperator), um Streams konzeptionell ähnlich einer for-Schleife zu erzeugen
  • ofNullable(T) um null-Elemente zu filtern und Nullchecks zu sparen
Short URL for this post: https://wp.me/p4nxik-34g
This entry was posted in Java Basics and tagged , . Bookmark the permalink.

Leave a Reply