Collections.sortがMergesortを使用し、Arrays.sortが使用しないのはなぜですか?


94

JDK-8(x64)を使用しています。Arrays.sort(プリミティブ)については、Javaのドキュメントで次のことがわかりました。

ソートアルゴリズムは、デュアルピボットであるクイックソートウラジミールYaroslavskiy、ジョン・ベントレー、とジョシュアBloch.`によって

Collections.sort(オブジェクト)の場合、この「Timsort」を見つけました:

この実装は、安定した適応可能な反復的なmergesortです...この実装は、指定されたリストを配列にダンプし、配列をソートし、リストを反復処理して、配列内の対応する位置から各要素をリセットします。

Collections.sort配列を使用している場合、なぜそれは単にArrays.sortデュアルピボットQuickSortを呼び出したり使用したりしないのですか?なぜ使うマージソートを


8
これがプリミティブの配列のjavadocです。オブジェクトの配列はmeregsortを使用してソートされます。
アッシリア2015

2
マージが与えるuはクイックソートがいつかギブnlogn2 geneallyアレイのサイズが大きいが、コレクションは簡単nlogn2のリスクを取るように、数百万のエントリ件まで行くことはないかもしれないが、常にnlognは、iがnのsqaure意味nlogn2価値はPSではありません
クマールSaurabh

クイックソートのO(n ^ 2)は極端な最悪の場合です。実際にはより高速です
James Wierzba '

しかし、APIを作成している間、それらのcaeseを無視することはできません
Kumar Saurabh

2
このリンクは非常に関連しています。
qartal 2016年

回答:


99

APIは、Quicksortが提供しない安定したソートを保証します。ただし、プリミティブ値を自然な順序で並べ替えると、プリミティブ値には同一性がないため、違いに気付くことはありません。したがって、Quicksortはプリミティブ配列に使用できより効率的であると考えられる場合に使用されます。

オブジェクトについては、equals実装または提供されたものに従って等しいと見なされる、異なるIDを持つオブジェクトのComparator順序が変更される場合があります。したがって、Quicksortはオプションではありません。したがって、MergeSortのバリアントが使用され、現在のJavaバージョンは TimSortをます。これは、両方に適用され、Arrays.sortそしてCollections.sort、Javaの8とが、Listそれ自体はソートアルゴリズムを無効にすることができます。


Quickクイックソートの効率上の利点は、インプレースで実行するときに必要なメモリが少ないことです。しかし、それは劇的な最悪のケースのパフォーマンスを持ち、配列内の事前にソートされたデータの実行を利用することはできません。これはTimSortが行います。

そのため、現在は誤解を招く名前が付けられたクラスにとどまりながら、ソートアルゴリズムがバージョンごとに作り直されましたDualPivotQuicksort。また、ドキュメントが追いつかなかったため、仕様で内部的に使用されているアルゴリズムに名前を付ける必要がない場合は、一般に悪い考えであることがわかります。

現在の状況(Java 8からJava 11を含む)は次のとおりです。

  • 一般に、プリミティブ配列のソート方法は、特定の状況でのみクイックソートを使用します。より大きなアレイの場合、TimSortが行うように、事前に並べ替えられたデータの実行を最初に識別しようとし、実行の数が特定のしきい値を超えないときにそれらをマージします。そうでなければ、クイックソートにフォールバックしますにフォールバックしますが、小さな配列の挿入ソートにフォールバックする実装では、小さな配列だけでなくクイックソートの再帰にも影響します。
  • sort(char[],…)sort(short[],…)使用する別の特別なケースを追加します、長さが特定のしきい値を超える配列に対してカウントソートします
  • 同様に、Counting sortsort(byte[],…)を使用しますが、はるかに小さいしきい値を使用します。これにより、sort(byte[],…)Quicksortを使用しないため、ドキュメントとの最大のコントラストが作成されます。小さな配列には挿入ソートのみを使用し、それ以外の場合はカウントソートを使用します。

1
興味深いことに、Collections.sort Javadocは「このソートは安定していることが保証されています」と述べていますが、Listの実装によってオーバーライドできるList.sortに委譲されているため、すべてのリストのCollections.sortによって安定したソートを保証することはできません。実装。それとも私は何かを逃していますか?また、List.sortは、ソートのアルゴリズムが安定している必要はありません。
2015

11
@Puce:つまり、その保証の責任は、オーバーライドList.sortメソッドを実装する人の手にかかっているということです。Collections.sortすべてのList実装が正しく機能することを保証することはできません。たとえば、Listが誤って内容を変更することがないためです。それはすべての保証ということに帰着Collections.sortだけが正しいにも適用されるListの実装(および正しいComparatorまたはequals実装)。
Holger

1
@Puce:しかし、あなたは正しい、Javadocは両方のメソッドでこの制約について等しく明示的ではありませんが、少なくとも最新のドキュメントでCollections.sortはに委任されると述べていますList.sort
Holger

@Puce:この例はたくさんありますが、重要なプロパティは型の一部ではなく、ドキュメントでのみ言及されています(したがって、コンパイラーによってチェックされません)。Javaの型システムは単純に弱すぎて、興味深いプロパティを表現できません。(これに関しては、動的に型付けされた言語とそれほど違いはありません。プロパティもドキュメントで定義されており、違反していないことをプログラマが確認する必要があります。)さらに、実際には:それCollections.sortは、出力がソートされていることを型シグネチャで言及していませんか?
イェルクWミッターク

1
より表現力豊かな型システムを持つ言語では、の戻り型は、Collections.sort「入力と同じ型と長さのプロパティを持つコレクションです。1)入力に存在するすべての要素は出力にも存在します2 )出力のすべての要素のペアについて、左側の要素は右側の要素よりも大きくない、3)出力の等しい要素のすべてのペアについて、入力の左側の要素のインデックスが右側の要素のインデックスよりも小さいなどそれ。
イェルクWミッターク

20

ドキュメントについてはわかりませんがjava.util.Collections#sort、Java 8(HotSpot)での実装は次のようになります。

@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(オブジェクト要素の)を舞台裏で使用します。この実装では、マージソートまたはtimソートを使用します。


16

Javadocによれば、プリミティブ配列のみがQuicksortを使用してソートされます。オブジェクト配列はMergesortでもソートされます。

つまり、Collections.sortは、オブジェクトのArrays.sortと同じ並べ替えアルゴリズムを使用しているようです。

もう1つの質問は、プリミティブ配列とオブジェクト配列では異なるソートアルゴリズムが使用される理由です。


2

答えの多くにわたって述べたように。

クイックソートは、安定性が必要ないため、Arrays.sortでプリミティブコレクションをソートするために使用されます(ソートで2つの同一のintが交換されたかどうかはわかりません)

MergeSortまたはより具体的にはTimsortは、オブジェクトのコレクションをソートするためにArrays.sortによって使用されます。安定性が必要です。Quicksortは安定性を提供しませんが、Timsortは安定性を提供します。

Collections.sortはArrays.sortにデリゲートするため、MergeSortを参照するjavadocが表示されます。


1

クイックソートには、マージソートに関して2つの大きな欠点があります。

  • 非プリミティブになると、安定しません。
  • n log nのパフォーマンスは保証されません。

(値)の等価性とは異なる同一性の概念がないため、安定性はプリミティブ型の問題ではありません。

任意のオブジェクトをソートする場合、安定性は重要です。Merge Sortが入力に関係なくn log n(時間)のパフォーマンスを保証することは良い副次的な利点です。これが、オブジェクト参照をソートするための安定したソート(マージソート)を提供するためにマージソートが選択されている理由です。


1
「不安定」とはどういう意味ですか?
Arun Gowda
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.