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. findFirst or findAny.

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 method.

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.

Bonus feature

The ofNullable aligns well with another new method introduced in the Optional API called stream().

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 Stream.ofNullable.

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 and dropWhile.

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

Looking at 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 (...)

Combining the takeWhile and dropWhile

If we now combine takeWhile and 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         

Final thought

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!

For more details, check out the documentation of the Stream API.

Enjoyed the post?

If you don't want to miss future posts, make sure to subscribe