【问题标题】:Encounter order preservation in java streamjava流中遇到订单保存
【发布时间】:2017-07-13 15:24:39
【问题描述】:

我已经解决了诸如How to ensure order of processing in java8 streams? 之类的相关问题,但输出元素的顺序对我来说并不完全清楚。因此,请澄清我的以下疑问。

 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 特征?

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

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

    对于像filtermap这样的无状态中间操作,无论如何都没有区别,但是像skiplimitdistinct这样的操作可能会表现出不同的行为,具体取决于之前的流状态是否被排序或无序。 This answer 显示了 distinct 如何受之前的 unordered() 影响的示例。

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

    This answer 提供了一种打印流特征并评估它们如何因附加另一个操作而发生变化的方法。

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

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

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

    所以

    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 特性的评估是在执行开始之前通过源、中间操作和终端操作的特性完成的吗?”的答案是,是的,这是在开始实际处理之前通过为流水线阶段选择适当的算法来完成的,如果有选择的话,但是这个过程不一定会导致整个流水线的单一特征。

    【讨论】:

    • 好答案,比我的要深入得多:)
    【解决方案2】:

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

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

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

    【讨论】:

    • forEachOrdered 还保证不会同时调用消费者,因此您可以说它会引入 some 顺序,而通过 forEach 的消费者可能会被调用一次有多个线程,所以它确实是无序的,尽管System.out.print 的内部同步也会在这种情况下强制执行 some 顺序……
    • @Holger 能否请您澄清一下相关问题是在什么时候决定保留遭遇订单的?
    猜你喜欢
    • 2018-08-21
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 2021-08-21
    • 2018-08-02
    • 2013-07-30
    相关资源
    最近更新 更多