stream包的JavaDoc说:“如果流的源是一个包含[1, 2, 3]的List,那么执行map(x -> x*2)的结果一定是[2, 4 ,6]。
我一直在尝试使用以下代码验证此行为,但输出与预期不符。
Optional<String> result = Stream.of(1, 2, 3, 4).parallel().map(i -> {
try {
if (i == 1) {
Thread.sleep(1000);
}
} catch (Exception e) {
}
return "" + i;
}).peek(x -> {
System.out.println(Thread.currentThread().threadId() + " Peeking " + x);
}).findFirst();
System.out.println(result);
输出为:
1 Peeking 3
23 Peeking 4
21 Peeking 2
22 Peeking 1
Optional[1]
map操作的输出流不应该先有“1”吗?
另外,即使我在并行之前插入 unordered() 操作,结果仍然是Optional1。为什么?在这种情况下,findFirst 不应该只返回任何值,而不是等待 1 的操作完成吗?
只有当我删除 unordered() 和 parallel() 操作时,我才会看到输出:
1 Peeking 1
Optional[1]
意思是,findFirst()确实短路了。但为什么流无序并行的时候不会短路呢?根据@user2357112评论,我通过将查看操作更改为映射来尝试如下:
map(x -> {
System.out.println("Remapping " + x);
return "x"+x;
})
输出仍然不按顺序:
Remapping 2
Remapping 4
Remapping 3
Remapping 1
Optional[x1]
parallel
会做什么?流元素上的
map
操作都是并行运行的。事实上,这项工作是相同的,并不意味着它们花费的时间完全相同。一个线程不必运行在同一个效率核心上。或者一个被打断而另一个没有,等等。因此,一旦并行运行,顺序就会消失。 您不会“首先”恢复
Optional[1]
,因为所有 4 个都是并行运行的。 Java 不是时间机器;它是时间机器。一旦它意识到“1”是一个可行的答案,它就不能返回并追溯更改时间,以便其他 3 个永远不会开始。 你可能会想:好吧,第一个(记住,没有“第一”,不是我们在谈论时
parallel()
,而是假设)将会通过,因为我们只是要求任何结果。但流 API 并不知道这一点。也许您打算对
filter
产生的任何内容运行
map
,这就是并行的全部意义:同时运行所有 4 个地图操作,然后同时对所有 4 个地图操作应用过滤。无论幸存者 - 首先拿走到达终点线(
.findFirst()
)的任何人并将其归还。为什么它返回一个Optional?为了同样的原因。如果
all 4 最终没有通过 filter
该怎么办?然后就没有元素了。您没有有
filter
这一事实并不重要。可能有一个。 API 是在编写时考虑到这一点的。尝试构建一种类型系统,它可以表示“流操作保证返回至少一个结果”和“流操作可能不返回任何结果”,然后看看您能走多远。 (你不会走得太远)。