JDK-8(x64)を使用しています。 Arrays.sort
(プリミティブ)の場合、Javaのドキュメントで次を見つけました。
ソートアルゴリズムは、デュアルピボットQuicksort Vladimir Yaroslavskiy、Jon Bentley、およびJoshua Blochによるものです。
Collections.sort
(オブジェクト)の場合、この「Timsort」が見つかりました。
この実装は、安定性、適応性、反復性ですmergesort ...この実装指定されたリストを配列にダンプし、配列をソートします配列内の対応する位置。
Collections.sort
が配列を使用する場合、なぜArrays.sort
を呼び出すか、デュアルピボットQuickSortを使用しないのですか? Mergesortを使用する理由
APIは、 Quicksort が提供しないstableソートを保証します。ただし、primitive valuesを自然な順序で並べ替えると、プリミティブ値には同一性がないため、違いに気付かないでしょう。したがって、 Quicksort はプリミティブ配列に使用でき、より効率的であると考えられる場合に使用されます¹。
equals
実装または提供されたComparator
に従って等しいとみなされる異なるIDを持つオブジェクトの順序が変わると、オブジェクトに気付くかもしれません。したがって、 Quicksort はオプションではありません。そのため、 MergeSort のバリアントが使用され、現在のJavaバージョンはTimSortを使用します=。これはArrays.sort
とCollections.sort
の両方に適用されますが、Java 8では、List
自体がソートアルゴリズムをオーバーライドする場合があります。
¹ Quicksort の効率の利点は、インプレースで実行するときに必要なメモリが少ないことです。ただし、劇的な最悪のケースのパフォーマンスがあり、配列内の事前に並べ替えられたデータの実行を活用することはできません TimSort が行います。
したがって、ソートアルゴリズムはバージョンからバージョンへと作り直され、今では誤解を招く名前のクラスDualPivotQuicksort
のままです。また、ドキュメントは追いついていませんでした。これは、一般的には、仕様で内部的に使用されるアルゴリズムに名前を付ける必要がない場合、それは悪い考えであることを示しています。
現在の状況(Java 8からJava 11を含む)は次のとおりです。
sort(char[],…)
およびsort(short[],…)
は、長さが特定のしきい値を超える配列に対して Counting sort を使用する別の特殊なケースを追加しますsort(byte[],…)
は Counting sort を使用しますが、sort(byte[],…)
はQuicksortを使用しないため、ドキュメントとの最大のコントラストを作成する非常に小さなしきい値を使用します。小さな配列には Insertion sort を使用し、それ以外の場合は Counting sort を使用します。ドキュメントについては知りませんが、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
(オブジェクト要素の)を使用します。この実装では、マージソートまたはティムソートが使用されます。
Javadocによると、プリミティブ配列のみがQuicksortを使用してソートされます。オブジェクト配列もMergesortでソートされます。
そのため、Collections.sortは、オブジェクトのArrays.sortと同じソートアルゴリズムを使用しているようです。
別の質問は、プリミティブ配列に対してオブジェクト配列とは異なるソートアルゴリズムが使用される理由ですか?
答えの多くで述べたように。
安定性は必要ないため、QuicksortはArrays.sortでプリミティブコレクションをソートするために使用されます(2つの同一のintがソートでスワップされたかどうかはわかりません)
MergeSortまたはより具体的にはTimsortは、オブジェクトのコレクションをソートするためにArrays.sortによって使用されます。安定性が必要です。 Quicksortは安定性を提供しませんが、Timsortは提供します。
Collections.sortはArrays.sortにデリゲートするため、MergeSortを参照するjavadocが表示されます。
クイックソートには、マージソートに関して2つの大きな欠点があります。
(値)の平等とは異なるアイデンティティの概念がないため、プリミティブ型では安定性は問題ではありません。
任意のオブジェクトを並べ替える場合、安定性は重要です。 Merge Sortは、入力が何であれ、n log n(時間)のパフォーマンスを保証するという素晴らしい副次的な利点です。これが、オブジェクト参照をソートする安定したソート(マージソート)を提供するためにマージソートが選択されている理由です。