流式管道的流畅表示法是导致这种混淆的原因。可以这样想:
limit(3)
所有流水线操作都懒惰地计算,除了 ,这是一个终端操作,它触发“流水线的执行”。forEach()
执行管道时,中间流定义不会对“之前”或“之后”发生的情况做出任何假设。他们所要做的就是获取输入流并将其转换为输出流:
Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9);
Stream<Integer> s2 = s1.peek(x->System.out.print("\nA"+x));
Stream<Integer> s3 = s2.limit(3);
Stream<Integer> s4 = s3.peek(x->System.out.print("B"+x));
s4.forEach(x->System.out.print("C"+x));
-
s1
包含 9 个不同的值。Integer
-
s2
查看传递它的所有值并打印它们。
-
s3
将前 3 个值传递到,并在第三个值之后中止管道。不会生成其他值。这并不意味着管道中没有更多值。s2
仍将生成(和打印)更多值,但没有人请求这些值,因此执行停止。s4
s3
-
s4
再次查看传递它的所有值并打印它们。
-
forEach
消耗并打印传递给它的任何内容。s4
这样想吧。整个流完全是懒惰的。只有终端操作会主动从管道中提取新值。从 中提取 3 个值后,将不再生成新值,也不再从 中提取任何值。虽然仍然能够产生,但这些值永远不会从管道中提取,因此永远不会被打印出来。s4 <- s3 <- s2 <- s1
s3
s2 <- s1
s1 -> s2
4-9
s2
skip(6)
同样的事情发生了:skip()
Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9);
Stream<Integer> s2 = s1.peek(x->System.out.print("\nA"+x));
Stream<Integer> s3 = s2.skip(6);
Stream<Integer> s4 = s3.peek(x->System.out.print("B"+x));
s4.forEach(x->System.out.print("C"+x));
-
s1
包含 9 个不同的值。Integer
-
s2
查看传递它的所有值并打印它们。
-
s3
使用前 6 个值,“跳过它们”,这意味着前 6 个值不会传递到 ,只有后续值是。s4
-
s4
再次查看传递它的所有值并打印它们。
-
forEach
消耗并打印传递给它的任何内容。s4
这里重要的是,不知道剩余的管道跳过任何值。 独立于之后发生的事情窥视所有值。s2
s2
另一个例子:
请考虑此管道,此博客文章中列出了此管道
IntStream.iterate(0, i -> ( i + 1 ) % 2)
.distinct()
.limit(10)
.forEach(System.out::println);
当您执行上述操作时,程序将永远不会停止。为什么?因为:
IntStream i1 = IntStream.iterate(0, i -> ( i + 1 ) % 2);
IntStream i2 = i1.distinct();
IntStream i3 = i2.limit(10);
i3.forEach(System.out::println);
这意味着:
-
i1
生成无限数量的交替值:、 、 、 、 ...0
1
0
1
0
1
-
i2
消耗以前遇到过的所有值,仅传递“新”值,即 总共有 2 个值来自 。i2
-
i3
传递 10 个值,然后停止。
此算法永远不会停止,因为 等待 在 和 之后再生成 8 个值,但这些值永远不会出现,而永远不会停止将值馈送到 。i3
i2
0
1
i1
i2
在管道中的某个时刻,已经产生了10多个值,这并不重要。重要的是从未见过这10个值。i3
要回答您的问题:
只是“跳过之前的每个操作都被执行,而限制之前的每个人都是”吗?
不。执行之前的所有操作或执行。在你的两次处决中,你都会得到 - .但可能会使管道短路,一旦发生感兴趣的事件(达到极限),就会中止值消耗。skip()
limit()
A1
A3
limit()