如何保证Java8流的处理顺序?

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

因此,我

是否应该在我使用的每个stream上调用sequentiallist.stream().sequential().filter().forEach()

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

136681 次浏览

你问错问题了。您询问的是sequentialparallel,而您要处理的是按顺序的项目,因此您必须询问订购。如果您有一个命令的流,并执行保证保持顺序的操作,则无论流是并行处理还是顺序处理都没有关系。执行将维持秩序。

有序属性不同于并行与顺序。例如,如果在HashSet上调用stream(),则流将是无序的,而在List上调用stream(),则返回有序流。请注意,您可以致电unordered(),以解除订购合同,并可能提高绩效。一旦流没有排序,就无法重新建立排序。(将无序流转换为有序流的唯一方法是调用sorted,但是,得到的顺序不一定是原始顺序)。

另请参阅java.util.stream包装文档“订购”部分

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

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

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

例如,如果您使用如下操作

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

整个操作可能会从并行执行中受益,但无论您使用的是并行流还是顺序流,结果列表都将始终处于正确的顺序中。

简而言之:

排序取决于源数据结构和中间流操作。假设您使用的是List,则应对处理进行排序(因为filter不会更改此处的顺序)。

更多详情:

顺序、并行、无序:

Javadoc

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.

流排序:

Javadoc

流可以具有也可以不具有定义的相遇顺序。 流是否具有相遇顺序取决于源。 和中间操作。某些流源(如列表 或数组)本质上是有序的,而其他(如HashSet) 不是。某些中间操作(如sorted())可能会强加一个 在其他无序流上遇到顺序,而其他流可能 呈现无序的有序流,如BaseStream.unordered()。 此外,一些终端操作可以忽略相遇顺序,例如 Foreach().

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

对于顺序流,存在或不存在相遇顺序 不影响性能,只影响确定性。如果流是有序的, 在相同的 源将产生相同的结果;如果没有订购, 重复执行可能产生不同的结果。

对于并行流,放松排序约束有时可以 实现更高效的执行。某些聚合操作,如 筛选重复项(DISTINCT())或分组缩减 (Collectors.GroupingBy())可以在以下情况下更有效地实现 元素的排序与此无关。同样,以下操作 本质上绑定到遭遇顺序,如limit(),可能需要 缓冲以确保正确排序,破坏了 平行度。在流具有相遇顺序的情况下, 用户并不特别明确地关心相遇顺序 使用unordered()对流进行反排序可以提高并行性 某些有状态或终端操作的性能。然而,大多数 流管道,如“块的重量总和”上面的例子, 即使在排序约束下仍然有效地并行化。