What's new in Java 9 Stream API?

Java 9 is finally released after a long wait. It has come up with lots of new features as well as enhancements to existing APIs. In this article, I’ll talk about the enhancements done in the Stream API.

Stream API was probably one of the most loved features of Java 8, and It has got better in Java 9 with the addition of four new methods - takeWhile(), dropWhile(), iterate(), and ofNullable().

Let’s take a look at these new methods and their usage one by one.

In this article, I’ll use JShell to run all the code samples. JShell is a REPL tool which is introduced in Java 9. You can learn more about JShell from my Introduction to Java 9 JShell.

Stream.takeWhile()

Take elements from a Stream while a predicate holds.

Stream.takeWhile() method takes elements from a Stream while a predicate holds. Once the predicate fails, it stops and returns the Stream.

Let’s see an example -

jshell> List<Integer> numbers = List.of(2, 4, 6, 7, 8, 10)
numbers ==> [2, 4, 6, 7, 8, 10]

jshell> numbers.stream().takeWhile(n -> n%2 == 0).forEach(System.out::println)
2
4
6

Notice how 8 and 10 are not part of the resulting Stream, even though they would pass the predicate. They are never tested because takeWhile() method stops when it encounters the number 7, which fails the predicate.

Using takeWhile() with an unordered Stream

List is an ordered collection. It preserves the order of elements.

In case of Streams obtained from ordered collections, takeWhile() method returns the longest prefix of elements that pass the predicate.

But what if it is applied to a Stream obtained from an unordered collection?

Consider a Set for example. Set is an unordered collection. It doesn’t preserve the order of elements. When you apply takeWhile() method to a Stream obtained from an unordered collection, the result is unpredictable, because you don’t know in what order the elements will be tested.

Here is an example -

jshell> Set<Integer> numbers = Set.of(2, 4, 6, 7, 8, 10)
numbers ==> [10, 2, 6, 7, 4, 8]

jshell> numbers.stream().takeWhile(n -> n%2 == 0).forEach(System.out::println)
10
2
6

If you run the above example multiple times in your machine, you’ll get different results every time.

Stream.dropWhile()

Drop elements from a Stream while a predicate holds.

Stream.dropWhile() is the opposite of Stream.takeWhile(). It drops elements from a Stream while a predicate holds. Once it encounters an element for which the predicate fails, It stops testing and returns that element and all the elements following that.

Check out the following example -

jshell> List<Integer> numbers = List.of(2, 4, 6, 7, 8, 10)
numbers ==> [2, 4, 6, 7, 8, 10]

jshell> numbers.stream().dropWhile(n -> n%2 == 0).forEach(System.out::println)
7
8
10

Since the predicate fails when 7 is encountered, dropWhile() stops testing and returns 7 and all the elements after 7.

Using dropWhile() with an unordered Stream

Just as we saw with takeWhile(), the output of dropWhile() method is also unpredictable for unordered Streams.

Check out the following example where we apply dropWhile() method to a Stream obtained from an unordered Collection -

jshell> Set<Integer> numbers = Set.of(2, 4, 6, 7, 8, 10)
numbers ==> [8, 10, 4, 7, 6, 2]

jshell> numbers.stream().dropWhile(n -> n%2 == 0).forEach(System.out::println)
7
6
2

Run the above example on your machine, and you might get different result than what I’ve got.

Stream.ofNullable()

Stream.ofNullable() takes an element and produces a Stream of single element if the specified element is non-null, otherwise an empty Stream -

// Produces a Stream of Single element
jshell> Stream.ofNullable("Hello").forEach(System.out::println)
Hello

// Produces an empty Stream
jshell> Stream.ofNullable(null).forEach(System.out::println) 

Stream.iterate()

Stream API already has an iterate() method which takes a seed value and a UnaryOperator and generates a Stream.

Following example will generate an infinite Stream of numbers 1,2,3,4,…

Stream.iterate(1, i -> i+1)

You can use limit() method on the above Stream to limit the number of elements generated by the iterate() method -

jshell> Stream.iterate(1, i -> i+1).limit(10).forEach(System.out::println)
1
2
3
4
5
6
7
8
9
10

This is fine but what if you want to limit the number of elements based on some condition instead of specifying a limit directly.

Well, This is exactly what Java 9’s overloaded version of the iterate() method does -

jshell> Stream.iterate(1, i -> i <= 10, i -> i+1).forEach(System.out::println)
1
2
3
4
5
6
7
8
9
10

The new overloaded version of the iterate() method takes a Predicate and keep generating elements until the predicate fails.

It is similar to the traditional for loop which takes a seed value, a condition, and an increment operation - for(int i = 1; i <= 10; i++).

Conclusion

Java 9 has done several improvements in existing APIs including Optional, Collection and Stream APIs.

In this article, we took a look at the improvements done in the Stream API. You might also wanna check out the following articles about the improvements in the Optional and Collection APIs -

That’s it, folks! Thank you for reading. See you in the next post.