为什么有序流上的映射操作不保留顺序?

问题描述 投票:0回答:1

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() 操作,结果仍然是Optional

1。为什么?在这种情况下,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]
    
java java-stream
1个回答
0
投票
你认为

parallel

会做什么?

流元素上的

map

 操作都是并行运行的。事实上,这项工作是相同的,
并不意味着它们花费的时间完全相同。一个线程不必运行在同一个效率核心上。或者一个被打断而另一个没有,等等。因此,一旦并行运行,顺序就会消失。

您不会“首先”恢复

Optional[1]

,因为
所有 4 个都是并行运行的。 Java 不是时间机器;它是时间机器。一旦它意识到“1”是一个可行的答案,它就不能返回并追溯更改时间,以便其他 3 个永远不会开始。

你可能会想:好吧,第一个(记住,没有“第一”,不是我们在谈论时

parallel()

,而是假设)
将会通过,因为我们只是要求任何结果。但流 API 并不知道这一点。也许您打算对 filter
 产生的任何内容运行 
map
,这就是并行的全部意义:同时运行所有 4 个地图操作,然后同时对所有 4 个地图操作应用过滤。无论幸存者 - 首先拿走到达终点线(
.findFirst()
)的任何人并将其归还。

为什么它返回一个Optional?为了同样的原因。如果

all 4 最终没有通过 filter

 该怎么办?然后就没有元素了。您没有
filter
这一事实并不重要。可能有一个。 API 是在编写时考虑到这一点的。尝试构建一种类型系统,它可以表示“流操作保证返回至少一个结果”和“流操作可能不返回任何结果”,然后看看您能走多远。 (你不会走得太远)。

© www.soinside.com 2019 - 2024. All rights reserved.