【问题标题】:Is .collect guaranteed to be ordered on parallel streams?.collect 是否保证在并行流上排序?
【发布时间】:2015-06-25 00:01:43
【问题描述】:

鉴于我有一个字符串列表List<String> toProcess。结果必须按照给出原始行的顺序。 我想利用新的并行流。

以下代码是否保证结果与原始列表中的顺序相同?

// ["a", "b", "c"]
List<String> toProcess;

// should be ["a", "b", "c"]
List<String> results = toProcess.parallelStream()
                                .map(s -> s)
                                .collect(Collectors.toList());

【问题讨论】:

    标签: java list java-8


    【解决方案1】:

    TL;DR

    是的,订单有保证。

    Stream.collect() API 文档

    开始的地方是看看是什么决定了减少是否是并发的。 Stream.collect() 的描述如下:

    如果流是并行的,并且Collectorconcurrent,并且流是无序的或者收集器是unordered,则将执行并发归约(有关并发归约的详细信息,请参阅Collector .)

    满足第一个条件:流是并行的。第二个和第三个呢:Collector是并发无序的吗?

    Collectors.toList() API 文档

    toList() 的文档内容如下:

    返回一个Collector,它将输入元素累积到一个新的List中。不保证返回的List 的类型、可变性、可序列化性或线程安全性;如果需要对返回的List 进行更多控制,请使用toCollection(Supplier)

    返回:
    一个收集器,将所有输入元素收集到一个列表中,按遇到顺序

    encounter order 中有效的操作以原始顺序对元素进行操作。这会覆盖并行性。

    实现代码

    检查Collectors.java 的实现确认toList() 确实包含CONCURRENTUNORDERED 特征。

    public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }
    
    // ...
    
    static final Set<Collector.Characteristics> CH_ID
            = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
    

    注意收集器是如何拥有 CH_ID 特征集的,它只有一个 IDENTITY_FINISH 特征。 CONCURRENTUNORDERED 不存在,因此减少不能并发。

    非并发归约意味着,如果流是并行的,则收集可以并行进行,但它将被拆分为几个线程限制的中间结果,然后将它们组合起来。这可确保组合结果按遇到顺序排列。

    另请参阅: Why parallel stream get collected sequentially in Java 8

    【讨论】:

    • 请注意,所有这些仅在流按顺序开始时才计算在内。如果它是无序的,则所有赌注都关闭,因为它没有内在的遭遇顺序。
    【解决方案2】:

    您可以保证按遭遇顺序获得元素。

    来自documentation of toList

    返回: 一个收集器,将所有输入元素收集到一个列表中,按遇到顺序

    请参阅java.util.streams summary,了解有关“遭遇订单”一词的更多信息。

    此外,List#spliterator 文档要求List 的所有实现都生成ORDERED 的拆分器:

    Spliterator 报告 Spliterator.SIZED 和 Spliterator.ORDERED。实施应记录附加特征值的报告。

    奇怪的是,虽然List 接口需要iterator() 以“正确的顺序”生成元素,但spliterator() 只需要排序,但并不特别需要遵循列表的自然顺序。

    因此,为了回答您的问题,toList 生成的列表保证包含与 源列表 的拆分器对它们排序的元素完全相同。流是并行还是顺序无关紧要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-20
      • 2018-12-11
      • 2012-02-09
      • 1970-01-01
      • 2021-03-21
      • 2014-07-22
      • 2014-09-29
      相关资源
      最近更新 更多