Java 9 - Making the Stream API even better
One of the first things I check out when looking at a new language is if it got a good collection API. I find that a good way of working with collections usually result in cleaner code that is easier to understand and reason about.
That's why I was glad to see the introduction of the Stream API in Java 8. Finally I could write simple yet powerful code when working with collections. That's why it's good to see that Java 9 brings even more features into the Stream API.
Let's take a look at what's added.
A new iterate method for creating streams
One of the ways to create streams is by using the
iterate method. Until now
iterate created infinite streams based on a seed and a function for generating the next element in the stream.
// an inifinite stream Stream.iterate(0, elem -> elem + 2); // Output: 0,2,4 (...infinite)
This stream would go on and on until we use another operation on our stream to create a condition - e.g.
In Java 9, we're introduced to a new variant of the
iterate method -- one that may not be infinite.
This is done by providing a hasNext predicate in addition to the seed and the function for creating the next element.
Stream.iterate(0, elem -> elem <= 10, elem -> elem + 2); // Output: 0,2,4,6,8,10
And that's it -- by providing a predicate
elem <= 10, we're creating a stream that will generate new elements as long as the new element is less then or equal to 10.
Creating a Stream of a value that may or may not be null
One typical way of creating a Stream is to use
Stream.of. The only thing you need to do with
Stream.of is to provide some values that you want to create a Stream of.
Integer element = 1; Stream.of(element); // Output stream: 1
The issue however is that if the element you provide is null, it'll create a Stream with an element null.
Integer element= null; Stream.of(element); // Output stream: null
This is not an ideal situation. Of course you could add a manual null check in front of the stream declaration, but what would be better was if the Stream API could take care of this for us.
With Java 9 this can be done by using the new
ofNullable will create a Stream of the element you provide if it's a non-null value. However, if it is null it'll simply create an empty stream.
Integer element= null; Stream.of(element); // Output stream: <empty stream>
Great! Now we got one less null value floating around in our system.
ofNullable aligns well with another new method introduced in the
Optional API called
What this method does is to convert an Optional to a Stream. If the Optional contains a non-null value, it'll become a stream of one element. However if the value it contains is null, it'll become an empty stream -- just like the
Filter out a subset of a Stream
Now that we've covered the new variants of creating a Stream, let's take a look at two new methods to use on our streams.
When working with streams, it's quite normal that you only want a subset of a stream. The ease of doing this has been improved by introducing two new methods --
takeWhile - returning a stream of elements as long as the predicate is fulfilled
An easy way to think about
takeWhile is that it keeps processing the stream as long as a certain condition is met.
Say we got an infinite stream going on, and each element is the previous element + 1. Now we could use
takeWhile to set a condition defining what would be an 'acceptable' element, then the stream would be processed as long as this condition fulfils -- for instance the element should be less than 10.
Stream.iterate(1, elem -> elem + 1) .takeWhile(element -> element < 10); // Output: 1,2,3,4,5,6,7,8,9
dropWhile - dropping elements from the stream as long as the predicate is fulfilled
dropWhile we see the quite opposite behaviour.
Instead of keeping elements in the stream as long as the predicate fulfils, it'll drop elements as long as the predicate fulfils.
The first element that doesn't match the predicate, will be the first element of the new stream.
If we visit our previous example, with the infinite stream of numbers, we'll see that by adding
dropWhile instead, we'll get a stream that starts at 10 and will never end.
Stream.iterate(1, elem -> elem + 1) .dropWhile(element -> element < 10); // Output: 10,11,12,13,14 (...)
If we now combine
dropWhile, we'll see that we can get a subset of the stream not only from the start or the end.
Let's say we want the first subset in the stream that doesn't contain a number less than 10 or more than 20.
Stream.iterate(1, elem -> elem + 1) .takeWhile(element -> element < 20) .dropWhile(element -> element < 10); // Output: 10,11,12,13,14,15,16,17,18,19
I think it's great that we see further enhancement on the Stream API. A good Collection API is very valuable, and if we look at what features out there in other languages, I think we could bring lots of more great additions to the Stream API. Let's see what the future brings!