Drop while a predicate returns true

Starting with JDK 9, we also have the Stream.dropWhile​(Predicate<? super T> predicate) method. This method is the opposite of takeWhile(). Instead of taking elements until the given predicate returns false, this method drops the elements until the given element returns false and includes the rest of the elements in the returned stream:

  • If the stream is ordered, it returns a stream consisting of the remaining elements of this stream after dropping the longest prefix of elements that match the given predicate.
  • If the stream is unordered, and some (but not all) of the elements of this stream match the given predicate, then the behavior of this operation is nondeterministic; it is free to drop any subset of matching elements (which includes the empty set).

In the case of an ordered Stream, the longest prefix of elements is a contiguous sequence of elements of the stream that match the given predicate.

For example, let's collect 5 integers after dropping the first 10:

List<Integer> result = IntStream
.iterate(1, i -> i + 1)
.dropWhile(i -> i <= 10)
.limit(5)
.boxed()
.collect(Collectors.toList());

This will always give the following output:

11, 12, 13, 14, 15

Alternatively, we can fetch a List of five random even integers greater than 50 (at least, this is what we may think the code does):

List<Integer> result = new Random().ints(1, 100)
.filter(i -> i % 2 == 0)
.dropWhile(i -> i < 50)
.limit(5)
.boxed()
.collect(Collectors.toList());

One possible output is as follows:

78, 16, 4, 94, 26

But why is 16 and 4 there? They are even, but not greater than 50! Well, they are there because they came after the first element, which failed the predicate. Mainly, we are dropping values while they are smaller than 50 (dropWhile(i -> i < 50)). The 78 value will fail this predicate, so dropWhile ends its job. Furthermore, all the generated elements are included in the result until limit(5) takes action.

Let's look at another similar trap. Let's fetch a List of five random passwords containing the ! character (at least, this is what we may think the code does):

List<String> result = Stream.generate(Main::randomPassword)
.dropWhile(s -> !s.contains("!"))
.limit(5)
.collect(Collectors.toList());

One possible output is as follows:

bab2!3dd, c2@$1acc, $c1c@cb@, !b21$cdc, #b103c21

Again, we can see passwords that don't contain the ! character. The bab2!3dd password will fail our predicate and will end up in the final result (List). The four generated passwords are added to the result without being influenced by dropWhile().

Now, let's assume that we have an unordered stream of integers. The following snippet of code drops a subset of elements that are less than or equal to 10 and keeps the rest:

Set<Integer> setOfInts = new HashSet<>(
Arrays.asList(5, 42, 3, 2, 11, 1, 6, 55, 9, 7));

List<Integer> result = setOfInts.stream()
.dropWhile(i -> i <= 10)
.collect(Collectors.toList());

One possible output is as follows (remember that, for an unordered stream, the result is nondeterministic):

55, 7, 9, 42, 11

If all the elements match the given predicate, then takeWhile() takes and dropWhile() drops all elements (it doesn't matter if the stream is ordered or unordered). On the other hand, if none of the elements match the given predicate, then takeWhile() takes nothing (returns an empty stream) and dropWhile() drops nothing (returns the stream).

Avoid using take/dropWhile() in the context of parallel streams since they are expensive operations, especially for ordered streams. If it is suitable for the case, then just remove the ordering constraint via BaseStream.unordered().
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.31.163