takeWhile, dropWhile 惰性 java9
takeWhile, dropWhile laziness java9
在 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" 个元素之后停止,它正在处理整个集合。
为什么要处理整个集合?我知道,它需要获取所有元素并将其收集到列表中,但它不应该在 "de" 元素之后停止处理吗?
这是预期的行为,与它在 Scala 中的工作方式相同,dropWhile 处理整个流。
dropWhile 是 takeWhile 的逆过程。当条件变为假时,takeWhile 停止处理。 dropWhile 处理整个流,但只在条件为真时不传递任何元素。一旦条件变为假,dropWhile 将传递所有剩余元素,无论条件是真还是假。
TL;DR
始终查看整个流,尤其是终端操作以判断管道的行为。 collect
对打印元素的影响与 takeWhile
或 dropWhile
.
一样大
详细
我不确定您是否不同意输出或结果列表。在任何情况下,处理流的既不是 takeWhile
也不是 dropWhile
,而是终端操作,在本例中为 collect
。它的任务是收集流中的所有元素,因此通过它收集 "pulls" 个元素,直到流报告不包含更多元素。
一旦条件第一次变成false
,takeWhile
就是这种情况。在报告没有更多元素剩余后,collect 停止提取元素,因此输入流的处理停止,来自 peek
.
的消息也停止。
不过 dropWhile
不同。在第一次请求它时,它 "fasts forward" 通过输入流,直到条件第一次变为 false
(这使得 peek
打印 a, b, c, de
。任何第一个元素它看到之后的操作是de
。从那时起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();
看来,对peek
的工作原理存在根本性的误解。它不与下一个后续链接操作相关联,如 dropWhile
,而是与它后面的整个 Stream 管道相关联。并且不区分“处理元素”和“取所有元素”
所以简单的代码
Stream.of("a", "b", "c", "de", "f", "g", "h")
.peek(System.out::println)
.collect(Collectors.toList());
“获取所有元素”,但在它们从流源传递到收集器时打印它们。
在您的示例中,无论元素是传递给 dropWhile
的谓词还是直接传递给 Collector
都没有区别,无论哪种情况,都会由 [= 报告13=]操作放在both.
之前
如果你使用
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
的传输从该元素开始。
这与 takeWhile
不同,其中 both,谓词求值和收集器停止消费元素,因此没有消费者留下,整个 Stream 管道可以停止迭代源。
我可能会在这里陈述显而易见的事情,但这对我来说很有意义,因此也可能对您有所帮助。
takeWhile 获取前 n 个元素,直到命中谓词(它 return 为真)。因此,如果您假设传入流中有 7 个元素,并且 takeWhile 对前 3 个元素 return 为真,则生成的流将包含 3 个元素。
你可以这样想:
Stream result = Stream.empty();
while(predicate.apply(streamElement)){ // takeWhile predicate
result.add(streamElement);
streamElement = next(); // move to the next element
}
return result;
dropWhile 表示删除元素直到谓词被命中(它 return 为真)。那么在删除 3 个元素之后,您的结果流会是什么样子?要知道我们需要迭代所有其他元素,这就是 peek 报告其余部分的原因。
我也喜欢从 Set(而不是 List)中 dropWhile 的类比。假设这是你的集合:
Set<String> mySet = Set.of("A", "B", "CC", "D");
你会在这里用相同的谓词做一个 dropWhile(因为它不是一个列表而且你没有遇到顺序);在 dropWhile 操作之后,除非您迭代剩下的所有元素,否则无法知道结果。
在 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" 个元素之后停止,它正在处理整个集合。
为什么要处理整个集合?我知道,它需要获取所有元素并将其收集到列表中,但它不应该在 "de" 元素之后停止处理吗?
这是预期的行为,与它在 Scala 中的工作方式相同,dropWhile 处理整个流。
dropWhile 是 takeWhile 的逆过程。当条件变为假时,takeWhile 停止处理。 dropWhile 处理整个流,但只在条件为真时不传递任何元素。一旦条件变为假,dropWhile 将传递所有剩余元素,无论条件是真还是假。
TL;DR
始终查看整个流,尤其是终端操作以判断管道的行为。 collect
对打印元素的影响与 takeWhile
或 dropWhile
.
详细
我不确定您是否不同意输出或结果列表。在任何情况下,处理流的既不是 takeWhile
也不是 dropWhile
,而是终端操作,在本例中为 collect
。它的任务是收集流中的所有元素,因此通过它收集 "pulls" 个元素,直到流报告不包含更多元素。
一旦条件第一次变成false
,takeWhile
就是这种情况。在报告没有更多元素剩余后,collect 停止提取元素,因此输入流的处理停止,来自 peek
.
不过 dropWhile
不同。在第一次请求它时,它 "fasts forward" 通过输入流,直到条件第一次变为 false
(这使得 peek
打印 a, b, c, de
。任何第一个元素它看到之后的操作是de
。从那时起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();
看来,对peek
的工作原理存在根本性的误解。它不与下一个后续链接操作相关联,如 dropWhile
,而是与它后面的整个 Stream 管道相关联。并且不区分“处理元素”和“取所有元素”
所以简单的代码
Stream.of("a", "b", "c", "de", "f", "g", "h")
.peek(System.out::println)
.collect(Collectors.toList());
“获取所有元素”,但在它们从流源传递到收集器时打印它们。
在您的示例中,无论元素是传递给 dropWhile
的谓词还是直接传递给 Collector
都没有区别,无论哪种情况,都会由 [= 报告13=]操作放在both.
如果你使用
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
的传输从该元素开始。
这与 takeWhile
不同,其中 both,谓词求值和收集器停止消费元素,因此没有消费者留下,整个 Stream 管道可以停止迭代源。
我可能会在这里陈述显而易见的事情,但这对我来说很有意义,因此也可能对您有所帮助。
takeWhile 获取前 n 个元素,直到命中谓词(它 return 为真)。因此,如果您假设传入流中有 7 个元素,并且 takeWhile 对前 3 个元素 return 为真,则生成的流将包含 3 个元素。
你可以这样想:
Stream result = Stream.empty();
while(predicate.apply(streamElement)){ // takeWhile predicate
result.add(streamElement);
streamElement = next(); // move to the next element
}
return result;
dropWhile 表示删除元素直到谓词被命中(它 return 为真)。那么在删除 3 个元素之后,您的结果流会是什么样子?要知道我们需要迭代所有其他元素,这就是 peek 报告其余部分的原因。
我也喜欢从 Set(而不是 List)中 dropWhile 的类比。假设这是你的集合:
Set<String> mySet = Set.of("A", "B", "CC", "D");
你会在这里用相同的谓词做一个 dropWhile(因为它不是一个列表而且你没有遇到顺序);在 dropWhile 操作之后,除非您迭代剩下的所有元素,否则无法知道结果。