Arguably one of the most noteworthy new features introduced in Java 8 was the Streams API. It allowed developers to declaratively manipulate collections using functional style programming. Since the release of Java 9 is just around the corner, let’s have a look at two new stream operations in Java 9—takeWhile
and dropWhile
.
takeWhile
takeWhile
is similar to filter
in the sense that it expects a predicate and returns a new stream consisting only of the elements that match the given predicate. But there’s a catch. In an ordered stream, takeWhile
takes elements from the initial stream while the predicate holds true
. Meaning that when an element is encountered that does not match the predicate, the rest of the stream is discarded. Let’s have a look at the following example.
In this example, we’re taking even numbers from the initial stream until the first odd number is encountered. The stream returned by takeWhile
contains 2
, 4
, 6
, 8
. It does not contain 9
since it did not match the predicate. Although 10
and 12
are even, they’re not included in the returned stream as well because the stream operation was cut off when 9
was encountered.
dropWhile
dropWhile
is essentially the opposite of takeWhile
. Instead of taking elements from the stream until the first element which does not match the predicate, dropWhile
drops these elements and includes the remaining elements in the returned stream.
The following is the same example we used previously with one noteworthy difference. takeWhile
has been replaced with dropWhile
.
In an ordered stream, dropWhile
removes the longest contiguous sequence of elements that match the given predicate. In this example we’re dropping even numbers. 2
, 4
, 6
and 8
are removed because applying the predicate on them returns true
. 9
isn’t an even number and is therefore included in the result. Even though 10
and 12
are even numbers, they’re included in the result because they came after the first element which failed the predicate.
Unordered streams
So far we’ve looked at how takeWhile
and dropWhile
behave with ordered streams. But what happens if the stream is unordered? According to the docs, if some of the elements in the stream match the predicate (but not all) then the operation is nondeterministic and an arbitrary subset of matching elements is returned or removed. Meaning that you’ll get different results for each execution.
You can expect a similar behavior if you replace takeWhile
with dropWhile
. It’s free to drop any subset of matching elements. That includes the empty set.
All elements match
Regardless of whether the stream is ordered or unordered, if all elements match the given predicate then takeWhile
takes and dropWhile
drops all elements. The result of takeWhile
is the same as the input stream. On the other hand, when all elements match, the result of dropWhile
will be an empty stream.
The following is an example of takeWhile
applied on an unordered stream where all the elements match the predicate.
No elements match
I bet you can already guess what happens if no elements match the given predicate. You’re right if you guessed that the result of takeWhile
will be an empty stream. Since no elements matched, there’s nothing to take. dropWhile
, on the other hand, returns the input stream if there’s nothing to drop.
Parallel streams
takeWhile
and dropWhile
are expensive operations on ordered parallel streams. Threads have to cooperate to find the longest contiguous sequence of matching elements in encounter order and this can significantly impact performance. Using a sequential stream may give you better results.
Summary
takeWhile
and dropWhile
are new additions to the Streams API introduced in Java 9. In ordered streams, they take or drop the longest contiguous sequence of elements from the stream based on the given predicate. However, in unordered streams, their behavior is nondeterministic. They’re free to take or drop any arbitrary subset of elements.