在 Java 流中遇到顺序保留



我已经经历了相关的问题,例如如何确保java8流中的处理顺序?,输出元素的顺序对我来说仍然不完全清楚。因此,请澄清我的以下疑问。

Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8 };
List<Integer> listOfIntegers =
new ArrayList<>(Arrays.asList(intArray));
listOfIntegers
.parallelStream()
.unordered()
.forEachOrdered(e -> System.out.print(e + " "));

我认为至少在理论上(或根据 java 规范(它可以以随机顺序打印,而不是 1、2、3、4、5、6、7、8。我说的对吗?

还有一个相关的问题——遭遇令保全的决定是在什么时候执行的? 更准确地说 - 整个流管道 ORDER 特性的评估是否通过在执行开始之前通过源、中间操作和终端操作的特征来完成?

源的无序性质或通过unordered()显式释放订单合约可能会影响所有后续管道阶段,除非它们引入了只能通过sorted操作发生的订单。

对于像filtermap这样的无状态中间操作,无论如何都没有区别,但是像skiplimitdistinct这样的操作可能会表现出不同的行为,具体取决于前一个流状态是有序的还是无序的。此答案显示了distinct如何受到先前unordered()影响的示例。

请注意,原则上,sorted在引入顺序时,可能取决于前一阶段的有序状态,因为如果前一个流是无序的,它可能会使用不稳定的排序算法。

此答案提供了一种打印流特征并评估它们如何因追加另一个操作而变化的方法。

当您链接终端操作时,终端操作本身的无序性质或终端操作之前最后一个阶段的无序状态可能足以为终端操作选择不尝试保留顺序的算法。

原则上,终端操作的无序性质可以用来影响前面的阶段,但由于无状态中间操作无论如何都不会受到影响,skiplimitdistinct必须遵守先前的有序状态,如果存在,唯一可能受影响的操作是sorted如果后续操作无论如何都不关心顺序,它就会过时。

在当前的实现中,自 Java 8 更新 60 以来,终端操作的无序性质不会影响前一阶段的行为。与以前的实现一样,进行了此更改,它错误地影响了skiplimit。失去消除过时的排序步骤的机会并不被认为是问题,因为将sort与无序的后续操作链接在一起是一个极端情况。如果您想了解有关相关讨论的更多信息,请参阅此答案,包括评论。

所以对于

list.stream() // List.stream() returns an ordered stream
.unordered() // releases order contract
.distinct() // for equal elements, it may pick an arbitrary one
.sorted() // re-introduces an order
.skip(1) // will skip the minimum element due to the order
.forEach(System.out::println); // may print the remaining elements in arbitrary order

流管道没有单个有序或无序行为。

相比之下,随着

hashSet.stream() // HashSet.stream() has no order (unless being a LinkedHashSet)
.filter(Objects::nonNull) // not affected by order
.distinct() // may use unorderedness, but has no effect anyway, as already distinct
.skip(1) // may skip an arbitrary element
.forEachOrdered(System.out::println); // would respect order if there was one

整个管道无序运行,只是因为源是无序的。使用有序来源,它将完全有序。

因此,"是否通过在执行开始之前通过遍历源、中间操作和终端操作的特征来评估整个流管道 ORDER 特征?"的答案是,是的,这是在开始实际处理之前完成的,通过为管道阶段选择合适的算法,当有选择时, 但此过程不一定会导致整个管道的单个特征。

一旦你选择了unordered那么最终结果基本上可以随机顺序出现。请注意,虽然没有要求它这样做,所以实际上您可能仍然会在输出中看到一些排序。

forEachOrdered保留了"流"的遭遇顺序,因此如果您没有.unordered()那么它将确保您看到遭遇顺序中的元素。如果流已经unordered,那么它毫无意义,您不妨使用forEach.

换句话说,forEachOrdered在已排序的流中保留遭遇顺序。它不进行任何排序或其他排序,因此如果流已经unordered那么任何事情都可能发生。

最新更新