takeWhile,dropWhile laziness java9

时间:2017-01-11 22:02:14

标签: java scala functional-programming java-stream java-9

在scala中,这些方法工作正常,但在java9中,dropWhile的工作原理与我的不同。

以下是takeWhile

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

输出很好: a,b,c,de,[a,b,c] 它不会在“de”之后处理元素,因此它按预期工作

但是DropWhile以我期望的不同方式工作:

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

输出是: a,b,c,de,f,g,h,[de,f,g,h]

因此它不会在“de”元素之后停止,它正在处理整个集合。

为什么要处理整个集合?我知道,需要获取所有元素并将其收集到List中,但不应该在“de”元素之后停止处理吗?

4 个答案:

答案 0 :(得分:11)

看来,peek的工作方式存在根本性的误解。它与下一个后续链接操作(如dropWhile)无关,而是与其后面的整个Stream管道相关联。它并没有区分“处理元素”和“占用所有元素”。

所以简单的代码

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

“获取所有元素”,但在将它们从Stream源传递到收集器时打印它们。

在您的示例中,无论是将元素传递给dropWhile的谓词还是直接传递给Collector,都没有区别,在任何一种情况下,它都会由{{1}报告放在两者之前的操作

如果您使用

peek

相反,它会打印

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());

显示dropWhile: a dropWhile: b dropWhile: c dropWhile: de collecting de collecting f collecting g collecting h 谓词的评估如何在第一个未接受的元素之后停止,而向dropWhile的转移从该元素开始。

这与Collector不同,其中两者,谓词评估和收集器,停止消耗元素,因此没有消费者离开,整个Stream管道可以停止迭代源。

答案 1 :(得分:6)

这是预期的行为,与scala中的工作方式相同,dropWhile处理整个流。

dropWhile与takeWhile相反。 takeWhile在条件变为false时停止处理。 dropWhile处理整个流,但只有在条件为真时才传递任何元素。一旦条件变为false,dropWhile会传递所有剩余元素,无论条件是真还是假。

答案 2 :(得分:4)

我可能会在这里陈述显而易见的事情,但这对我来说很有意义,所以也可以帮助你。

takeWhile 获取前n个元素,直到命中Predicate(它返回true)。因此,如果您在输入流中说出7个元素并且takeWhile将在前3个中返回true,则生成的流将包含3个元素。

你可以这样想:

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

dropWhile 表示删除元素直到命中Predicate(它返回true)。在删除3个元素之后,结果流会是什么样子?要知道我们需要迭代所有其他元素,这就是peek报告其余元素的原因。

我也喜欢从Set(而不是List)中dropWhile的类比。假设这是你的设置:

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

哟你会用同一个谓词做一个dropWhile(因为它不是List而你没有遭遇命令);在dropWhile操作之后,除非你迭代剩下的所有元素,否则无法知道结果。

答案 3 :(得分:3)

TL; DR

始终查看整个流,尤其是终端操作,以判断管道的行为方式。 collect影响打印元素的次数与takeWhiledropWhile一样多。

详细信息

我不确定您是否不同意输出或结果列表。在任何情况下,它都不是takeWhiledropWhile正在处理流,而是终端操作,在这种情况下是collect。它的任务是收集流中的所有元素,因此&#34;拉动&#34;通过它的元素,直到流报告不再包含元素。

第一次条件成为takeWhilefalse就是这种情况。在报告不再剩余元素之后,collect停止拉动元素,因此输入流的处理停止,来自peek的消息也停止。

但与dropWhile不同。在第一次请求时,它会快速前进&#34;通过输入流,直到条件第一次变为false(这使得peek打印a, b, c, de。它看到的任何操作的第一个元素是de。从那时起on collect继续通过流拉取元素,直到报告为空为止,这是输入流以h结束的情况,这导致peek的其余输出。

使用以下内容进行试用,结果应为a, b, c, de [de]。 (我很有把握,我甚至没有尝试过。;))

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