Stream.sorted() 然后收集,还是收集然后 List.sort()?
Stream.sorted() then collect, or collect then List.sort()?
总的来说,这两段代码在性能上有区别吗?
List<Integer> list1 = someStream1.sorted().collect(toList());
// vs.
List<Integer> list2 = someStream2.collect(toList());
list2.sort(Comparator.naturalOrder())
变体 2 显然令人讨厌,应该避免,但我很好奇是否有任何性能优化内置于主流(嘿,mainstream)的 Stream 实现中会导致这两者之间的性能差异。
我想因为流有更多关于情况的信息,它会有更好的优化机会。例如。我想如果这有一个附加的 findFirst()
调用,它会省略排序,有利于 min
操作。
从概念上讲,流通常被视为 "transient" 正在 processed/manipulated 的数据,并且收集流传达了您已完成对它的操作的想法。
虽然第二个片段应该有效,但第一个片段是更惯用的做事方式。
这两个选项的最终结果应该相同。但是运行时特性 可能 不同。如果初始流是 parallel 流怎么办?然后选项 1 将并行排序,而选项 2 不会执行 "sequential" 排序。结果应该是相同的,但整体运行时间 resp。 CPU 那时负载可能会有很大不同。
与选项 2 相比,我绝对更喜欢选项 1:为什么要先创建一个列表,然后稍后 对其进行排序?!
假设您稍后想要收集到一个 immutable 列表中。然后所有遵循您的第二个模式的代码都会中断。而使用模式 1 编写的代码根本不会受到影响!
当然,在此处的示例中,这不会导致问题,但如果 sort() 发生在稍微不同的地方怎么办?!
不保证您从 Collectors.toList()
返回的列表是可编辑的。它可能是一个ArrayList,或者一个ImmutableList,你不知道。因此,您不得尝试修改该列表。
在第一种情况下,排序发生在对 collect
的调用中。如果流已经排序,这将是一个空操作(数据将按原样传递)。可能不会有太大区别,但在已排序的集合上调用 Collections.sort
仍然是 O(n)。
第一种情况也受益于并行执行,因为至少 OpenJDK 使用 Arrays.parallelSort
。
除此之外,第一行更清晰,更易于理解,并且在重构时更不容易出错。
根据文档,第一种排序似乎不是无序流的稳定排序实现:
For ordered streams, the sort is stable. For unordered streams, no stability guarantees are made.
但第二个是稳定的排序实现:
This implementation is a stable, adaptive, iterative mergesort that requires far fewer than n lg(n) comparisons when the input array is partially sorted, while offering the performance of a traditional mergesort when the input array is randomly ordered. If the input array is nearly sorted, the implementation requires approximately n comparisons.
所以,排序算法的稳定性是这两种列表排序方法的区别之一。
总的来说,这两段代码在性能上有区别吗?
List<Integer> list1 = someStream1.sorted().collect(toList());
// vs.
List<Integer> list2 = someStream2.collect(toList());
list2.sort(Comparator.naturalOrder())
变体 2 显然令人讨厌,应该避免,但我很好奇是否有任何性能优化内置于主流(嘿,mainstream)的 Stream 实现中会导致这两者之间的性能差异。
我想因为流有更多关于情况的信息,它会有更好的优化机会。例如。我想如果这有一个附加的 findFirst()
调用,它会省略排序,有利于 min
操作。
从概念上讲,流通常被视为 "transient" 正在 processed/manipulated 的数据,并且收集流传达了您已完成对它的操作的想法。
虽然第二个片段应该有效,但第一个片段是更惯用的做事方式。
这两个选项的最终结果应该相同。但是运行时特性 可能 不同。如果初始流是 parallel 流怎么办?然后选项 1 将并行排序,而选项 2 不会执行 "sequential" 排序。结果应该是相同的,但整体运行时间 resp。 CPU 那时负载可能会有很大不同。
与选项 2 相比,我绝对更喜欢选项 1:为什么要先创建一个列表,然后稍后 对其进行排序?!
假设您稍后想要收集到一个 immutable 列表中。然后所有遵循您的第二个模式的代码都会中断。而使用模式 1 编写的代码根本不会受到影响!
当然,在此处的示例中,这不会导致问题,但如果 sort() 发生在稍微不同的地方怎么办?!
不保证您从 Collectors.toList()
返回的列表是可编辑的。它可能是一个ArrayList,或者一个ImmutableList,你不知道。因此,您不得尝试修改该列表。
在第一种情况下,排序发生在对 collect
的调用中。如果流已经排序,这将是一个空操作(数据将按原样传递)。可能不会有太大区别,但在已排序的集合上调用 Collections.sort
仍然是 O(n)。
第一种情况也受益于并行执行,因为至少 OpenJDK 使用 Arrays.parallelSort
。
除此之外,第一行更清晰,更易于理解,并且在重构时更不容易出错。
根据文档,第一种排序似乎不是无序流的稳定排序实现:
For ordered streams, the sort is stable. For unordered streams, no stability guarantees are made.
但第二个是稳定的排序实现:
This implementation is a stable, adaptive, iterative mergesort that requires far fewer than n lg(n) comparisons when the input array is partially sorted, while offering the performance of a traditional mergesort when the input array is randomly ordered. If the input array is nearly sorted, the implementation requires approximately n comparisons.
所以,排序算法的稳定性是这两种列表排序方法的区别之一。