If you’d like to take only a certain number of elements from a stream or skip some of them, you can invoke the
skip(m) methods. But what if you need to take or skip some elements until some condition is true? Starting from Java 9, streams have two convenient methods to do this. They can take or drop the longest contiguous subsequence of elements from the stream based on the given predicate.
The takeWhile method
takeWhile method takes elements from the stream until the first inappropriate element is encountered. This element and all the rest are discarded. Which element is inappropriate is determined by the predicate passed to this method. If all elements will match the predicate, the result will contain the same elements as the initial stream.
In the following example, we create a stream of numbers and take elements while they are greater than zero.
List<Integer> numbers = Stream.of(3, 5, 1, 2, 0, 4, 5) .takeWhile(n -> n > 0) .collect(Collectors.toList()); System.out.println(numbers); // [3, 5, 1, 2]
takeWhile method stops after taking the element
0, because the condition becomes
We believe that the method is clear enough not to dwell on it for a long time.
The dropWhile method
There is also an opposite method called
dropWhile. It drops the elements which match the given predicate until the first element does not match it. This and all the remaining elements are included in the result. If all elements will match the predicate, the result will be an empty stream.
Here is the same example as before, but with
dropWhile instead of
List<Integer> numbers = Stream.of(3, 5, 1, 2, 0, 4, 5) .dropWhile(n -> n > 0) .collect(Collectors.toList()); System.out.println(numbers); // [0, 4, 5]
dropWhile method stops dropping right after taking the element 0, because the condition becomes
false. The elements
5 remain in the stream.
The case of unordered streams
dropWhile work well in case of ordered streams. Such streams are created from ordered collections (e.g. lists) or obtained during operations (like sorting). But what if we are dealing with an unordered collection such as a set? It turns out that in this case the behavior of both operations is nondeterministic.
As an example, suppose we have a set of Java conferences with one Kotlin conference. We can try to keep taking conferences names as long as they start with
"J" and stop at the first inappropriate conference.
Set<String> conferences = Set.of( "JokerConf", "JavaZone", "KotlinConf", "JFokus" ); conferences.stream() .takeWhile(word -> word.startsWith("J")) .forEach(System.out::println);
Since we don’t know the order of the names in the set, the result of this code may always be different, containing from 0 to 3 conference names. This is definitely not what we wanted here.
dropWhilewith unordered streams leads to nondeterministic behavior, and as a result, to bugs.
How to achieve repeatable results in this case?
- to use
- to add the
takeWhile()to keep always the same order of elements within the stream.
This concludes our consideration of taking / dropping elements from a stream.