【发布时间】:2018-12-07 07:24:31
【问题描述】:
我关心从包含几千个元素的 Java PriorityQueue 创建排序数组的不同样式。 Java 8 docs 说
如果您需要有序遍历,请考虑使用 Arrays.sort(pq.toArray())。
不过,我确实喜欢流式 API,所以我最初拥有的是
Something[] elems = theHeap.stream().sorted(BY_CRITERION.reversed())
.toArray(Something[]::new);
(其中BY_CRITERION 是 PriorityQueue 的自定义比较器,我确实想要它的相反顺序。)与以下相比,使用此成语有什么缺点:
Something[] elems = theHeap.toArray(new Something[0]);
Arrays.sort(elems, BY_CRITERION.reversed());
后一个代码显然更直接地遵循 API 文档建议,但除此之外,它在内存方面是否真的更有效,例如分配的临时结构更少等?
我认为流式解决方案必须将流元素缓冲在一个临时结构(数组?)中,然后对它们进行排序,最后将排序后的元素复制到toArray()中分配的数组中。
虽然命令式解决方案会将堆元素缓冲在新分配的数组中,然后对它们进行排序。所以这可能会少一个复制操作。 (还有一个数组分配。Collection.toArray(new T[size]) 与 Collection.toArray(new T[0]) 的讨论与此处相关。例如,请参阅 here 了解为什么在 OpenJDK 上后者更快。)
那么分拣效率呢? Arrays.sort() 的文档说
临时存储要求从几乎排序的输入数组的小常数到随机排序的输入数组的 n/2 对象引用不等
Stream.sorted() 的文档在这一点上保持沉默。因此,至少在可靠记录方面,命令式解决方案似乎具有优势。
但是还有什么要知道的吗?
【问题讨论】:
-
不使用流几乎总是性能更好的选择(除非您使用
.parallel()和拥有数百万条目大小的数据) -
如果优先队列已经按照排序顺序为您提供了元素,为什么还要再次排序。
-
用
BY_CRITERION.reversed()作为比较器初始化priorityQueue -
@SleimanJneidi 因为它不是。只有 peek() 和 poll() 尊重比较器。没有有序遍历的方法。这是在文档中指定的。
-
@Sebastian 关于流的要点是它们表达了它们的什么,而不是如何它们这样做。在内部,他们使用了很多对象,在这里和那里创建新的引用。看起来很好。但就性能而言,正如串行所说的那样,至少不是最佳选择。而使用数组方法只创建一个数组,然后对其进行排序。不再对其执行任何操作。这就是它更快的原因
标签: java arrays sorting java-stream priority-queue