Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

takeWhile, dropWhile laziness java9

In scala those methods works fine but in java9 dropWhile works differently I think.

Here is example for takeWhile

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

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

But dropWhile works in different way that 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 is: a, b, c, de, f, g, h, [de, f, g, h]

So it's not stopping after "de" element, it's processing whole collection.

Why is it processing whole collection? I know, that it's needed to take all elements and collect it to the List but shouldn't it stop processing after "de" element?

like image 414
user7284379 Avatar asked Jan 11 '17 22:01

user7284379


1 Answers

It seems, there is a fundamental misunderstanding about how peek works. It is not associated with the next subsequently chained operation, like dropWhile, but the entire Stream pipeline behind it. And it does not make a distinction between “processing elements” and “take all elements”.

So the simple code

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

“takes all elements”, but prints them as they are passed from the Stream source to the Collector.

In your example, it makes no difference, whether an element is passed to the predicate of dropWhile or directly to the Collector, in either case, it will be reported by the peek operation placed before 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, it will print

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

showing how the evaluation of dropWhile’s predicate stops after the first unaccepted element whereas the transfer to the Collector starts with that element.

This differs from the takeWhile where both, the predicate evaluation and the collector, stop consuming elements, so there is no consumer left and the entire Stream pipeline can stop iterating the source.

like image 125
Holger Avatar answered Sep 17 '22 18:09

Holger