在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”元素之后停止处理吗?
答案 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)
始终查看整个流,尤其是终端操作,以判断管道的行为方式。 collect
影响打印元素的次数与takeWhile
或dropWhile
一样多。
我不确定您是否不同意输出或结果列表。在任何情况下,它都不是takeWhile
和dropWhile
正在处理流,而是终端操作,在这种情况下是collect
。它的任务是收集流中的所有元素,因此&#34;拉动&#34;通过它的元素,直到流报告不再包含元素。
第一次条件成为takeWhile
时false
就是这种情况。在报告不再剩余元素之后,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();