【问题标题】:Stream.sorted() then collect, or collect then List.sort()? [duplicate]Stream.sorted() 然后收集,还是收集然后 List.sort()? [复制]
【发布时间】:2019-02-26 04:10:21
【问题描述】:

一般来说,这两段代码之间是否存在性能差异?

List<Integer> list1 = someStream1.sorted().collect(toList());
// vs.
List<Integer> list2 = someStream2.collect(toList());
list2.sort(Comparator.naturalOrder())

变体 2 显然很糟糕,应该避免,但我很好奇 Stream 的主流(呵呵,mainstream)实现中是否内置了任何性能优化,这会导致性能这两者的区别。

我想,因为流有更多关于情况的信息,所以它会有更好的优化机会。例如。我想如果这有一个findFirst() 调用,它会省略排序,有利于min 操作。

【问题讨论】:

  • @FedericoPeraltaSchaffner 啊,是的,关注这个问题很好。我把它改成了Integer,这样Comparable的实现就知道了。我还更改了流名称,以明确我没有重复相同的流两次(这是无效的)
  • @FedericoPeraltaSchaffner 或者只是list.sort(null)
  • @shmosel 很有趣,谢谢......从来没有注意到这也是有效的......认为会抛出NullPointerException :)
  • 恕我直言,斯蒂芬的回答对于理解What is more efficient: sorted stream or sorting a list? 的区别确实更有意义。此外,由于链接问题中也有基准,我会投票将其标记为重复。 (我敢肯定没有多少人会第二:D)

标签: java list sorting java-stream collectors


【解决方案1】:

从概念上讲,流通常被视为正在处理/操作的“瞬态”数据,收集流传达了您已完成操作的概念。

虽然第二个 sn-p 应该可以工作,但第一个会是更惯用的做事方式。

【讨论】:

    【解决方案2】:

    这两个选项应该产生相同的最终结果。但运行时特性可能有所不同。如果初始流是并行流怎么办?然后选项 1 将并行进行排序,而选项 2 不会进行“顺序”排序。结果应该是相同的,但整体运行时分别。那时 CPU 负载可能会有很大不同。

    我肯定更喜欢选项 1 而不是 2:为什么要先创建一个列表,然后 稍后 对其进行排序?!

    想象一下,例如,您稍后想要收集到一个 不可变 列表中。然后,遵循您的第二种模式的所有代码都会中断。而使用模式 1 编写的代码根本不会受到影响!

    当然,在这里的示例中应该不会导致问题,但是如果 sort() 发生在稍微不同的地方怎么办?!

    【讨论】:

    • 如果流可以支持排序和收集到不可变列表中,这是否意味着流有自己的暂存缓冲区进行排序,然后必须进行复制?
    • @Alexander 正确,当前 Stream 实现总是使用内部缓冲区进行排序,这意味着如果您确保结果列表是可变的并且具有就地排序支持,第二个变体会稍微快一些.由于Collectors.toList() 两者都不保证,因此您必须改用Collectors.toCollection(ArrayList::new)。但这排除了从对Collectors.toList() 进行的潜在未来改进中受益。而且你不应该高估复制成本。在 Java 8 之前,Collections.sort 总是进行 两次 复制操作,几乎没有人注意到……
    【解决方案3】:

    不保证您从Collectors.toList() 返回的列表是可编辑的。它可能是一个ArrayList,或者一个ImmutableList,你不知道。因此,您不得尝试修改该列表。

    【讨论】:

    • 好点,但技术性。您可以通过toCollection(ArrayList::new) 获取可变列表。
    【解决方案4】:

    在第一种情况下,排序发生在对collect 的调用中。如果流已经排序,这将是一个无操作(数据将按原样传递)。可能没有太大区别,但在已经排序的集合上调用 Collections.sort 仍然是 O(n)。

    第一种情况也受益于并行执行,至少OpenJDK 使用Arrays.parallelSort

    除此之外,第一行更简洁,更易于理解,重构时不易出错。

    【讨论】:

      【解决方案5】:

      根据文档,似乎第一个排序对于无序流来说不是一个稳定的排序实现:

      对于有序流,排序是稳定的。对于无序流,不保证稳定性。

      但第二个是稳定的排序实现:

      此实现是一种稳定的、自适应的、迭代的归并排序,当输入数组部分排序时,它需要远少于 n 次 lg(n) 的比较,同时在输入数组是随机排序时提供传统归并排序的性能。如果输入数组接近排序,则实现需要大约 n 次比较。

      所以,排序算法的稳定性是这两种列表排序方法的区别之一。

      【讨论】:

      • 我认为你混淆了第一个和第二个 sn-ps。无论哪种方式,最终的结果都是一样的。如果流是无序的,则在第二种情况下生成的列表将是无序的,因此排序稳定无济于事。
      • 排序不稳定并不意味着结果不一定会排序。这只是意味着两个相等的对象可以相互重新排序。例如,如果输入是[John Smith, Jane Smith, Joe Brown],并且我们仅根据姓氏进行排序,则不稳定的排序可能会产生[Joe Brown, Jane Smith, John Smith]
      • 感谢您的所有 cmets,我的朋友们。根据您提到的几点,我对我的答案进行了一些更改。
      猜你喜欢
      • 2012-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-02
      • 2016-11-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多