Core Java

Java 9 Additions To Stream

Java 9 is coming! And it is more than just Project Jigsaw. (I was surprised, too.) It is bringing a lot of small and not-so-small changes to the platform and I’d like to look at them one by one. I’ll tag all these posts and you can find them here.

Let’s start with …

Streams

Streams learned two new tricks. The first deals with prefixes, which streams now understand. We can use a predicate to test a stream’s elements and, starting at the beginning, either take or drop them until the first fails a test.

Stream::takeWhile

Let’s look at takeWhile first:

Stream<T> takeWhile(Predicate<? super T> predicate);

Called on an ordered stream it will return a new one that consists of those element that passed the predicate until the first one failed. It’s a little like filter but it cuts the stream off as soon as the first element fails the predicate. In its parlance, it takes elements from the stream while the predicate holds and stops as soon as it no longer does.

Let’s see an example:

Stream.of("a", "b", "c", "", "e")
	.takeWhile(s -> !String.isEmpty(s));
	.forEach(System.out::print);
 
Console: abc

Easy, right? Note how e is not part of the returned stream, even though it would pass the predicate. It is never tested, though, because takeWhile is done after the empty string.

Prefixes

Just to make sure we’re understanding the documentation, let’s get to know the terminology. A subsequence of an ordered stream that begins with the stream’s first element is called a prefix.

Stream<String> stream = Stream.of("a", "b", "c", "d", "e");
Stream<String> prefix = Stream.of("a", "b", "c");
Stream<String> subsequenceButNoPrefix = Stream.of("b", "c", "d");
Stream<String> subsetButNoPrefix = Stream.of("a", "c", "b");

The takeWhile-operation will return the longest prefix that contains only elements that pass the predicate.

Prefixes can be empty so if the first element fails the predicate, it will return the empty stream. Conversely, the prefix can be the entire stream and the operation will return it if all elements pass the predicate.

Order

Talking of prefixes only makes sense for ordered streams. So what happens for unordered ones? As so often with streams, the behavior is deliberately unspecified to enable performant implementations.

Taking from an unordered stream will return an arbitrary subset of those elements that pass the predicate. Except if all of them do, then it always returns the entire stream.

Concurrency

Taking from an ordered parallel stream is not the best idea. The different threads have to cooperate to ensure that the longest prefix is returned. This overhead can degrade performance to the point where it makes more sense to make the stream sequential.

java-9-stream

Published by Andy Arthur under CC-BY 2.0.

Stream::dropWhile

Next is dropWhile:

Stream<T> dropWhile(Predicate<? super T> predicate);

It does just the opposite of takeFirst: Called on an ordered stream it will return a new one that consists of the first element that failed the predicate and all following ones. Or, closer to its name, it drops elements while the predicate holds and returns the rest.

Time for an example:

Stream.of("a", "b", "c", "de", "f")
	.dropWhile(s -> s.length <= 1);
	.forEach(System.out::print);
 
Console: def

Note that the stream contains f even though it would not pass the predicate. Analog to before, the operation stops after the first string fails the predicate, in this case ef.

Called on an unordered stream the operation will drop a subset of those elements that fail the predicate. Unless all of them do, in which case it will always return an empty stream. Everything else we said above about terminology and concurrency applies here as well.

Stream::ofNullable

That one’s really trivial. Instead of talking about it, lets see it in action:

long one = Stream.ofNullable("42").count();
long zero = Stream.ofNullable(null).count();

You got it, right? It creates a stream with the given element unless it is null, in which case the stream is empty. Yawn!

It has its use cases, though. Before, if some evil API gave you an instance that could be null, it was circuitous to start operating on a stream that instance could provide:

// findCustomer can return null
Customer customer = findCustomer(customerId);
 
Stream<Order> orders = customer == null
	? Stream.empty()
	: customer.streamOrders();
// do something with stream of orders ...
 
// alternatively, for the Optional lovers
Optional.ofNullable(customer)
	.map(Customer::streamOrders)
	.orElse(Stream.empty()
	. // do something with stream of orders

This gets much better now:

// findCustomer can return null
Customer customer = findCustomer(customerId);
 
Stream.ofNullable(customer)
	.flatMap(Customer::streamOrders)
	. // do something with stream of orders

Reflection

We’ve seen how takeWhile will return elements that pass the predicate and cut the stream off when the first element fails it. Conversely, dropWhile will also cut the stream when the first element fails the predicat but will return that one and all after it.

As a farewell, let’s see a final example, in which we stream all lines from an HTML file’s meta element:

Files.lines(htmlFile)
	.dropWhile(line -> !line.contains("<meta>")
	.skip(1)
	.takeWhile(line -> !line.contains("</meta>")

We also learned about ofNullable. I wonder why it seems so familiar? Ah yes, Optional of course! Coincidently I will cover that next. :)

Stay tuned!

Reference: Java 9 Additions To Stream from our JCG partner Nicolai Parlog at the CodeFx blog.

Nicolai Parlog

Nicolai is a thirty year old boy, as the narrator would put it, who has found his passion in software development. He constantly reads, thinks, and writes about it, and codes for a living as well as for fun.Nicolai is the editor of SitePoint's Java channel, writes a book about Project Jigsaw, blogs about software development on codefx.org, and is a long-tail contributor to several open source projects. You can hire him for all kinds of things.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button