在Java Stream中保留遇到顺序

3

我已经阅读了类似于如何确保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的随机顺序打印。我的理解正确吗?

还有一个相关的问题 - 在什么时候决定保留遇到顺序? 更准确地说,是在执行开始之前通过遍历源、中间操作和结束操作的特性来评估整个流管道的顺序特征吗?

2个回答

6

源的无序性或通过 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特性?”的答案是肯定的。在有选择的情况下,会选择适当的算法来处理流水线阶段,但这个过程不一定会导致整个流水线具有单一的特性。请注意,HTML标签应该被保留。

2
很好的回答,比我的更深入 :) - Tim B

2
一旦您选择了unordered,结果可能以随机顺序出现。请注意,实际上没有要求它这样做,因此实际上您仍然可能在输出中看到一些排序。 forEachOrdered保留了“流”遇到的顺序,因此如果您没有使用.unordered(),则它将确保您按照遇到的顺序查看元素。但是,如果流已经是unordered,那么这是无意义的,您可以使用forEach
换句话说,forEachOrdered在已经排序的流中保留了遇到的顺序。它不进行任何排序或其他排序,因此如果流已经是unordered,则任何事情都可能发生。

2
forEachOrdered 还保证了消费者不会并发调用,因此可以说它会引入一些顺序,而传递给 forEach 的消费者可能会被多个线程同时调用,因此它是真正的无序的,尽管 System.out.print 的内部同步在这种情况下也会强制执行一些顺序... - Holger
@Holger,能否请您明确一下相关问题,在什么时候决定保留遭遇顺序? - nantitv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接