TakeWhile, dropWhile laziness java9

In scala, these methods work fine, but in java9 dropWhile works differently, I think.

Here is an example for takeWhile

Stream.of("a", "b", "c", "de", "f", "g", "h") .peek(System.out::println) .takeWhile(s -> s.length() <= 1) .collect(Collectors.toList()); 

The output is beautiful: a, b, c, de, [a, b, c] It does not process elements after "de", so it works as expected

But dropWhile works differently what I expect:

 Stream.of("a", "b", "c", "de", "f", "g", "h") .peek(s -> System.out.print(s + ", ")) .dropWhile(s -> s.length() <= 1) .collect(Collectors.toList()); 

Output: a, b, c, de, f, g, h, [de, f, g, h]

Thus, it does not stop after the de element, it processes the entire collection.

Why does he handle the entire collection? I know that he needed to take all the elements and collect them in the List, but should he not stop processing after the "de" element?

+6
source share
4 answers

There seems to be a fundamental misunderstanding of how peek works. It is not associated with the next sequential operation like dropWhile , but behind all this thread. And he does not distinguish between "processing elements" and "accepts all elements."

So simple code

 Stream.of("a", "b", "c", "de", "f", "g", "h") .peek(System.out::println) .collect(Collectors.toList()); 

"accepts all elements", but prints them as they are transferred from the Stream source to Collector.

In your example, it does not matter if the element is passed to the dropWhile predicate or directly to the Collector , in any case it will be reported by the peek operation placed in front of both.

If you use

 Stream.of("a", "b", "c", "de", "f", "g", "h") .dropWhile(s -> { System.out.println("dropWhile: "+s); return s.length() <= 1; }) .peek(s -> System.out.println("collecting "+s)) .collect(Collectors.toList()); 

instead he will print

 dropWhile: a dropWhile: b dropWhile: c dropWhile: de collecting de collecting f collecting g collecting h 

shows how the dropWhile s predicate stops after the first unaccepted element, while the transfer to Collector begins with this element.

This differs from takeWhile in that both predicate evaluation and the collector stop consumption elements, therefore there is no consumer on the left, and the entire stream pipeline can stop source iteration.

+10
source

This is the expected behavior and identical to how it works in scala, dropWhile processes the entire stream.

dropWhile is the opposite of takeWhile. takeWhile stops processing when the condition becomes false. dropWhile processes the entire stream, but only does not pass any elements while the condition is true. As soon as the condition becomes false dropWhile passes all other elements regardless of whether the condition is true or false.

+6
source

TL DR

Always look at the entire flow, especially at the terminal operation, to judge how the pipeline behaves. This collect affects printed items as much as takeWhile or dropWhile .

In detail

I am not sure if you agree with the output or the resulting list. In any case, this is not takeWhile and dropWhile , which processes the stream, but a terminal operation, collect in this case. It must collect all the elements in the stream and, therefore, “pulls” the elements through it until the stream contains more elements.

This is the case with takeWhile , when the condition becomes false for the first time. After he reports that there are more elements left, collect stops by pulling the elements, and therefore the input stream process stops, as well as messages from peek .

However, this is different from dropWhile . At the first request to it, it “accelerates forward” through the input stream until the condition becomes false for the first time ( peek print a, b, c, de does this). The first element that any operation after viewing it is de From now on, collect continues to pull the element through the stream until it is declared empty, which will happen when the input stream terminates with h , which will lead to the rest of the output from peek .

Try the following: a, b, c, de [de] . (I'm so sure I didn’t even try.;))

 Stream.of("a", "b", "c", "de", "f", "g", "h") .peek(s -> System.out.print(s + ", ")) .dropWhile(s -> s.length() <= 1) .findFirst(); 
+4
source

I will probably state the obvious here, but it makes sense to me, so it can help you.

takeWhile takes the first n elements until a predicate is reached (it will return true). So, if you let say 7 elements in your input stream and takeWhile will return true for the first 3, then the resulting stream will contain 3 elements.

You can think of it this way:

 Stream result = Stream.empty(); while(predicate.apply(streamElement)){ // takeWhile predicate result.add(streamElement); streamElement = next(); // move to the next element } return result; 

dropWhile says to reduce the elements until the predicate is reached (it will return true). So, after resetting 3 elements, what will your result look like? To know that we need to iterate over all the other elements, that is why peek reports everything else.

I also like the dropWhile analogy from the set (as opposed to the list). Suppose this is your set:

  Set<String> mySet = Set.of("A", "B", "CC", "D"); 

And yo will do dropWhile here with the same predicate (because it's not a list, and you don't have an order to meet); after the dropWhile operation, there is no way to find out the result unless you iterate through all the elements left there.

+2
source

Source: https://habr.com/ru/post/1013999/


All Articles