如何保证java8流中的处理顺序?

2022-08-31 06:31:37

我想处理Java对象中的列表。我必须确保按照收到的顺序处理所有元素。XML

因此,我应该调用我使用的每个吗?sequentialstreamlist.stream().sequential().filter().forEach()

或者只要我不使用并行性,就足以使用流?list.stream().filter().forEach()


答案 1

你问错了问题。您正在询问 vs. 而您希望按顺序处理项目,因此您必须询问订购问题。如果您有一个有流并执行保证保持顺序的操作,则无论流是并行处理还是顺序处理都无关紧要;实施将维持秩序。sequentialparallel

有序属性不同于并行与顺序。例如,如果您调用一个流将是无序的,而调用返回一个有序的流。请注意,您可以调用 unordered() 来释放订购合约并可能提高性能。一旦流没有排序,就无法重新建立排序。(将无序流转换为有序流的唯一方法是调用 ,但是,生成的顺序不一定是原始顺序)。stream()HashSetstream()Listsorted

另请参阅 java.util.stream 包文档“排序”部分

为了确保在整个流操作中维护排序,您必须研究流源的文档,所有中间操作和终端操作,以确定它们是否保持顺序(或者源是否首先具有排序)。

这可能非常微妙,例如 Stream.iterate(T,UnaryOperator) 创建一个有序流,而 Stream.generate(Supplier) 创建无流。请注意,您在问题中也犯了一个常见错误,因为 forEach 维护顺序。如果要按保证的顺序处理流的元素,则必须使用 forEachOrdered

因此,如果您的问题确实是 一个 ,其方法将返回有序流,并且不会更改排序。因此,如果您调用 ,则所有元素将按顺序顺序处理,而对于元素,这些元素可能并行处理(例如,由过滤器处理),但终端操作仍将按顺序调用(这显然会降低并行执行的好处)。listjava.util.Liststream()filterlist.stream().filter() .forEachOrdered()list.parallelStream().filter().forEachOrdered()

例如,如果您使用类似

List<…> result=inputList.parallelStream().map(…).filter(…).collect(Collectors.toList());

整个操作可能受益于并行执行,但无论您使用的是并行流还是顺序流,生成的列表都将始终按正确的顺序排列。


答案 2

简而言之:

排序取决于源数据结构和中间流操作。假设您正在使用处理应该进行排序(因为不会更改此处的顺序)。Listfilter

更多详情:

顺序 vs 并行 vs 无序:

爪哇岛

S sequential()
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
This is an intermediate operation.
S parallel()
Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.
This is an intermediate operation.
S unordered()
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
This is an intermediate operation.

流排序:

爪哇岛

流可能具有定义的遭遇顺序,也可能没有。流是否具有遭遇顺序取决于源和中间操作。某些流源(如 List 或数组)本质上是有序的,而其他流源(如 HashSet)则不是。某些中间操作(如 sorted())可能会对其他无序流施加遇到顺序,而其他操作可能会使有序流无序呈现,例如 BaseStream.unordered()。此外,某些终端操作可能会忽略遇到顺序,例如 forEach()。

如果对流进行排序,则大多数操作被限制为按其相遇顺序对元素进行操作;如果流的源是包含 [1, 2, 3] 的列表,则执行 map(x -> x*2) 的结果必须是 [2, 4, 6]。但是,如果源没有定义的遭遇顺序,则值 [2, 4, 6] 的任何排列都将是有效的结果。

对于顺序流,遇到顺序的存在与否不会影响性能,只会影响确定性。如果对流进行排序,则在相同源上重复执行相同的流管道将产生相同的结果;如果未排序,则重复执行可能会产生不同的结果。

对于并行流,放宽排序约束有时可以实现更有效的执行。如果元素排序不相关,则可以更有效地实现某些聚合操作,例如筛选重复项 (distinct()) 或分组缩减 (Collectors.groupingBy())。类似地,与遭遇顺序有内在联系的操作(如 limit())可能需要缓冲来确保正确的排序,从而破坏了并行性的好处。如果流具有遭遇顺序,但用户并不特别关心该遭遇顺序,则使用 unordered() 显式取消对流的排序可能会提高某些有状态或终端操作的并行性能。但是,大多数流管道(如上面的“块的权重总和”示例)即使在排序约束下也能有效地并行化。


推荐