我正在执行以下程序:
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.forEach(s -> System.out.println("forEach: " + s));
我得到的输出是:
filter: d2
forEach: d2
filter: a2
forEach: a2
filter: b1
forEach: b1
filter: b3
forEach: b3
filter: c
forEach: c
但是,我期待以下输出:
filter: d2
filter: a2
filter: b1
filter: b3
filter: c
forEach: d2
forEach: a2
forEach: b1
forEach: b3
forEach: c
意思是,首先filter
方法循环应该已经完全执行,然后forEach
方法循环应该已经开始。
我有什么问题吗?
答案 0 :(得分:8)
你的期望是错误的。
执行终端操作forEach
时,它一次消耗Stream
的一个元素。它消耗的每个元素都必须传递Stream
的所有中间操作,这导致在filter
在同一元素上执行之前的元素上执行forEach
(假设元素通过filter
)。
换句话说,当filter
管道中的下一个操作需要其下一个元素(在您的情况下,下一个操作是Stream
)时,forEach
会被懒惰地应用于每个元素。
这意味着如果您的终端操作只需要处理某些Stream元素(例如,如果您将forEach()
替换为findFirst()
),则filter()
操作只会执行直到第一个元素通过它(在你的例子中意味着只对第一个元素执行过滤器)。
答案 1 :(得分:1)
Stream的处理将从终端操作开始,并逐个使用一个项目。优点是:
该计划必须计算仅相关项 - 例如,如果只需要一个项目,则filter
仅被调用一次:
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.findAny()
.ifPresent(s -> System.out.println("forEach: " + s));
输出:
filter: d2
forEach: d2
你甚至可以创建 infinte Streams(否则这是不可能的):
IntStream.iterate(0, i -> i+1)
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.peek(i -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
})
.forEach(s -> System.out.println("forEach: " + s));
输出:
filter: 0
forEach: 0
filter: 1
forEach: 1
... (up to infinity)