遇到顺序友好/不友好的终端操作 vs 并行/顺序 vs 有序/无序流

2024-01-07

灵感来自这个问题 https://stackoverflow.com/questions/44944973/search-for-example-of-inconsistent-behavior-java-8-stream,我开始研究有序流与无序流、并行流与顺序流以及尊重顺序的终端操作与不尊重顺序的终端操作。

在链接问题的一个答案中,显示了与此类似的代码:

List<Integer> ordered = Arrays.asList(
    1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4);
List<Integer> result = new CopyOnWriteArrayList<>();

ordered.parallelStream().forEach(result::add);

System.out.println(ordered);
System.out.println(result);

而且列表确实不同。这unorderedlist 甚至从一次运行到另一次运行都会发生变化,表明结果实际上是不确定的。

所以我创建了另一个例子:

CopyOnWriteArrayList<Integer> result2 = ordered.parallelStream()
        .unordered()
        .collect(Collectors.toCollection(CopyOnWriteArrayList::new));

System.out.println(ordered);
System.out.println(result2);

我预计会看到类似的结果,因为流既是并行的又是无序的(也许unordered()是多余的,因为它已经是并行的)。但是,结果列表是有序的,即它等于源列表。

所以我的问题是为什么收集的列表是有序的?做collect始终尊重遭遇顺序,即使对于并行、无序的流也是如此?是具体的吗Collectors.toCollection(...)收集者是强制遭遇秩序的那个吗?


Collectors.toCollection返回一个Collector其中缺少Collector.Characteristics.UNORDERED特征。另一个指定的收集器Collector.Characteristics.UNORDERED可能会有不同的行为。

也就是说:“无序”意味着没有保证, not 保证会有所不同。如果图书馆发现最容易将无序集合视为有序集合,则允许这样做,并且允许该行为在星期二或满月时将版本更改为版本。

(另请注意Collectors.toCollection如果您要使用并行流,则不需要您使用并发集合实现;toCollection(ArrayList::new)会工作得很好。那是因为收藏家没有Collector.Characteristics.CONCURRENT特征,因此它使用的收集策略适用于非并发收集,即使是并行流。)

如果您使用无序流但收集器不是UNORDERED,反之亦然,我怀疑你从框架中得到任何保证。如果有一个表,它会显示“这里是DRAGONS未定义的行为”。我还期望这里不同类型的链式操作存在一些差异,例如尤金提到findFirst这里有所不同,尽管findFirst本质上是一个有序的操作——unordered().findFirst()变得等于findAny().

For Stream.collect,我相信当前的实现可以选择三种策略:

  • 顺序:启动一个累加器,将元素累加到其中(按遇到顺序,因为为什么要费心去打乱元素?只需按照获得它们的顺序接受它们),调用完成器。
  • 并行执行、并发收集器以及流或收集器是无序的:一个累加器对输入进行分片,工作线程处理每个分片中的元素,并在准备好时将元素添加到累加器,调用完成器。
  • 并行执行,其他:将输入分片为 N 个分片,每个分片按顺序累积到自己的分片中distinct累加器,累加器与组合器函数组合,调用完成器。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

遇到顺序友好/不友好的终端操作 vs 并行/顺序 vs 有序/无序流 的相关文章

随机推荐