【问题标题】:Does intermediate operations honors encounter order when terminal operation used in the same stream pipeline does not honors encounter order?当在同一流管道中使用的终端操作不遵守顺序时,中间操作是否遵守顺序?
【发布时间】:2019-09-04 15:47:03
【问题描述】:

如果我在列表(作为源)上使用 forEach() 终端操作(无论其顺序流还是并行流不遵守遇到顺序)的流管道中使用映射操作,则映射尊重遇到顺序在顺序或并行流的情况下的列表?

List<Integer> = Arrays.asList(1,2,3,4,5)
someList.stream().map(i -> i*2).forEach(System.out::println) // this is sequential stream
someList.parallelStream().map(i -> i*2).forEach(System.out::println) // this is parallel stream

如果是,那么在https://stackoverflow.com/a/47337690/5527839这个帖子中,提到地图操作将并行执行。如果保持顺序,使用并行流时如何使性能更好。使用并行流有什么意义?

【问题讨论】:

  • "如果订单保持不变,它将如何使性能更好" - 不会。保持并行流中的顺序可能会降低性能。并行流不是灵丹妙药,它们不会自动改善一切。
  • 另外:中间操作本身并不真正“尊重遇到顺序” - 中间操作(不幸的是 .sorted().distinct() 例外)只关心一个单一元素(hense它是一个“管道”),不需要关心之前或之后会出现什么元素。遇到订单通常只是终端操作的事情。
  • 好的...谢谢@M.Prokhorov 为我解惑:)
  • 是的,我忘了这两个。它们并不那么明显,但根据经验,如果中间操作隐藏了一些内部状态 - 它会关心遇到顺序。这使得流中的有状态与无状态中间体的比例约为 50/50。
  • @M.Prokhorov 从 Java 9 开始,takeWhiledropWhile 也将取决于遭遇顺序。

标签: java java-8 java-stream


【解决方案1】:

如果保持顺序,使用并行流时如何使性能更好。使用并行流有什么意义?(是的,您仍然会获得性能但不是预期的水平)

即使你使用forEachOrdered()parallelStream中间操作map会被并发线程执行,但在终端操作orEachOrdered使它们按顺序处理。试试下面的代码,你会看到map操作中的并行性

List<Integer> someList = Arrays.asList(1,2,3,4,5);
            someList.stream().map(i -> {
                System.out.println(Thread.currentThread().getName()+" Normal Stream : "+i);
                return i*2;
            }).forEach(System.out::println); // this is sequential stream

            System.out.println("this is parallel stream");

            someList.parallelStream().map(i -> {
                System.out.println(Thread.currentThread().getName()+" Parallel Stream : "+i);
                return i*2;
            }).forEachOrdered(System.out::println); // this is parallel stream

地图荣誉会遇到顺序吗?排序是否与中间操作有关?

如果是parallelstream map 不会遇到任何顺序,如果是普通流则 map 会顺序遇到,完全依赖流而不是中间操作

【讨论】:

  • 按照你所说的,我可以断言map 不遵守遇到的订单,无论终端操作是否尊重遇到的订单,也不管它是并行流还是顺序流?
  • 你试过我的代码了吗,我在map 操作中添加了一个打印语句,它们在并行流中是否按顺序打印? @PiyushKumar
  • 是的,地图正在以并行和顺序的顺序打印。但是对于并行流,您使用了forEachOrdered 终端操作,它遵循遇到的顺序。但问题是,如果终端操作不遵循遇到顺序并且我正在使用以列表为源的映射中间操作(这是有序集合因此创建有序流)...会映射荣誉遇到顺序吗?排序是否与中间操作相关?
  • 常见map如何按顺序打印而parallelStream,对于这个问题will map honor encounter order ? Is ordering any way related to intermediate operations ?如果是并行流map不会遇到任何顺序,如果是正常流那么map会按顺序遇到,它完全取决于流而不是中间操作@PiyushKumar
  • 规范中没有任何内容表明map处理顺序 将永远匹配遇到顺序,无论流是并行还是并行顺序的。这是一个特定于实现的副作用,两个顺序似乎都与顺序执行相匹配。
【解决方案2】:

虽然许多中间操作确实保留了排序而不必明确指定该愿望,但我总是更喜欢假设流经 java 流 api 的数据不能保证在每个场景中最终都排序,即使给定相同的代码。

当必须保留元素的顺序时,将终端操作指定为有序操作就足够了,出来的时候数据是有序的。在你的情况下,我相信你正在寻找

.forEachOrdered()

如果保持秩序,如何让表现更好? 使用并行流。使用并行流有什么意义?

我听到了很多关于这个的意见。我相信只有在管道内进行大量处理时才应该使用并行流,否则与串行流相比,管理并行流的开销在大多数情况下会降低性能。如果你正在做一些密集的处理,即使被指示保留顺序,并行仍然肯定比串行工作得更快,因为毕竟,正在处理的数据以任何一种方式存储在堆中,并且指向该数据的指针是被排序的和从管道的末端流出。流排序所需要做的就是按照遇到的相同顺序将指针传递出去,但它可以并行处理数据,如果队列前面的数据还没有完成,就等待。

我确信这有点过于简单化了,因为在某些情况下,有序流需要数据从一个元素共享到下一个元素(例如收集器)但基本概念是有效的,因为即使在在这种情况下,并行流一次能够处理至少两条数据。

【讨论】:

    猜你喜欢
    • 2021-06-11
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    相关资源
    最近更新 更多