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

时间:2015-03-23 17:29:05

标签: java java-8 java-stream

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

因此,我应该在我使用的每个sequential上致电stream吗? list.stream().sequential().filter().forEach()

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

3 个答案:

答案 0 :(得分:278)

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

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

另请参阅“Ordering” sectionjava.util.stream package documentation

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

这可能非常微妙,例如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());

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

答案 1 :(得分:1)

简而言之:

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

更多详细信息:

顺序vs并行vs无序:

Javadocs

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.

流排序:

Javadocs

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

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

对于顺序流,是否存在遇到顺序 不影响性能,仅影响确定性。如果订购了流, 在相同的对象上重复执行相同的流管线 源将产生相同的结果;如果没有订购, 重复执行可能会产生不同的结果。

对于并行流,有时可以放宽排序约束 使执行效率更高。某些汇总操作,例如 过滤重复项(distinct())或分组归约 如果(Collectors.groupingBy())可以更有效地实现 元素的顺序无关紧要。同样, 本质上与遇到顺序有关,例如limit()可能需要 缓冲以确保适当的订购,从而损害了 并行性。如果流具有遇到顺序,但是 用户并不特别在乎那个碰头顺序 使用unordered()对流进行排序可以改善并行 有状态或终端操作的性能。但是,大多数 流管道,例如上面的“块权重之和”示例, 即使在排序约束下仍然可以有效地并行化。

答案 2 :(得分:-2)

  

list.stream()。顺序()。过滤()。的forEach()

将始终按顺序处理列表,因为列表本身是有序的。

但是如果我们使用

  

list.stream()。平行()

然后其余操作无法保证订单。