Java 9 is coming! And it is more than justProject 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 themhere.
Let’s start with …
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.
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.
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.
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.
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 .
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.
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 Customercustomer = 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 Customercustomer = findCustomer(customerId); Stream.ofNullable(customer) .flatMap(Customer::streamOrders) . // do something with stream of orders
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. 🙂
Share & Follow
You liked this post? Then share it with your friends and followers!
And if you like what I’m writing about, why don’t you follow me?