是否保证在并行流上订购 .collect?

2022-08-31 17:02:52

鉴于我有一个字符串列表。结果必须按照给出原始行的顺序排列。我想利用新的并行流。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());

答案 1

TL;DR

是的,订单是有保证的。

Stream.collect() API documentation

首先要看是什么决定了减少是否是并发的。Stream.collect() 的描述如下:

如果流是并行的,并且是并发的,并且流是无序的或收集器是无序的,则将执行并发减少(有关并发减少的详细信息,请参阅收集器)。Collector

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

Collectors.toList() API 文档

toList() 的文档内容如下:

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

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

遭遇顺序工作的操作按元素的原始顺序对元素进行操作。这将覆盖并行性。
 

实现代码

检查 的实现确认不包括 或 特征。Collectors.javatoList()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_IDIDENTITY_FINISHCONCURRENTUNORDERED

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

另请参阅:为什么在 Java 8 中按顺序收集并行流


答案 2

保证您按遭遇顺序获取元素。

toList 的文档:

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

有关术语“遭遇顺序”的更多信息,请参阅 java.util.streams 摘要

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

Spliterator 报告 Spliterator.SIZE 和 Spliterator.Ordered。实现应记录其他特征值的报告。

奇怪的是,虽然界面需要以“正确的顺序”生成元素,但只需要排序,而不是特别要求遵循列表的自然顺序。Listiterator()spliterator()

因此,为了回答您的问题,可以保证生成的列表完全按照源列表的拆分器对元素进行排序来包含这些元素。流是并行的还是顺序的并不重要。toList