为什么Collections.sort使用Mergesort但是Arrays.sort却没有?

时间:2015-09-01 14:31:15

标签: java arrays sorting collections java-8

我正在使用JDK-8(x64)。对于Arrays.sort(原语),我在Java文档中找到了以下内容:

  

排序算法是Vladimir Yaroslavskiy,Jon Bentley和Joshua Bloch的Dual-Pivot Quicksort

对于Collections.sort(对象),我发现了这个“Timsort”:

  

此实现是一个稳定的,自适应的,迭代的 mergesort ...此实现将指定的列表转储到数组中,对数组进行排序,并迭代列表重置来自数组中相应位置的每个元素。

如果Collections.sort使用数组,为什么不调用Arrays.sort或使用双轴 QuickSort ?为什么要使用 Mergesort

5 个答案:

答案 0 :(得分:82)

API保证了Quicksort不提供的稳定排序。但是,当按照自然顺序对原始值进行排序时,您不会注意到差异,因为原始值没有标识。因此,Quicksort用于原始数组,因为它的效率稍高。

对于您可能会注意到的对象,当根据equals实现或提供的Comparator认为相同的对象更改其顺序时。因此,Quicksort不是一个选择。因此使用MergeSort的变体,当前的Java版本使用 TimSort 。这同样适用于Arrays.sortCollections.sort,但使用Java 8时,List本身可能会覆盖排序算法。

答案 1 :(得分:18)

我不知道文档,但Java 8(HotSpot)中java.util.Collections#sort的实现是这样的:

@SuppressWarnings({"unchecked", "rawtypes"})
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

List#sort有这个实现:

@SuppressWarnings({"unchecked", "rawtypes"})
default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

因此,最后,Collections#sort在幕后使用Arrays#sort(对象元素)。此实现使用合并排序或时间排序。

答案 2 :(得分:15)

根据Javadoc,只使用Quicksort对原始数组进行排序。对象数组也使用Mergesort进行排序。

所以Collections.sort似乎使用与Arrays.sort for Objects相同的排序算法。

另一个问题是为什么原始数组使用不同的排序算法而不是Object数组?

答案 3 :(得分:2)

正如许多答案所述。

Quicksort被Arrays.sort用于对原始集合进行排序,因为不需要稳定性(您不知道或不关心在排序中是否交换了两个相同的整数)

MergesSort或更具体地说,Arrays.sort使用Timsort来排序对象集合。需要稳定性。 Timsort确实没有提供稳定性。

Collections.sort委托给Arrays.sort,这就是你看到引用MergeSort的javadoc的原因。

答案 4 :(得分:1)

快速排序在合并排序时有两个主要缺点:

  • 它涉及非原始的不稳定。
  • 它不保证n log n性能。

稳定性对于原始类型来说不是问题,因为没有不同于(值)相等的身份概念。

在对任意对象进行排序时,稳定性是一个大问题。无论输入是什么,Merge Sort都能保证n log n(时间)性能,这是一个很好的附带好处。 这就是选择合并排序以提供稳定排序(合并排序)以对对象引用进行排序的原因。