Collections.sortがクイックソートではなくマージソートを使用するのはなぜですか?


100

クイックソートが最速のソートアルゴリズムであることはわかっています。

JDK6 collections.sortは、クイックソートではなくマージソートアルゴリズムを使用します。ただし、Arrays.sortはクイックソートアルゴリズムを使用します。

Collections.sortがクイックソートではなくマージソートを使用する理由は何ですか?


3
JDKの作者に答えてもらえない限り、当てはまるのは推測作業だけです。本当の質問ではありません。
ローンの侯爵

4
@EJP良い点ですが、確かに「建設的ではない」が正しい閉鎖理由です。ここで質問が何であるかは私には明らかです。
ダンカンジョーンズ

2
Javaの人たちはこのようにすることにしました。それらを尋ねます。ここでは正当な答えは得られないと思います。また、クイックソートは最適ではありません一般的な用途にのみ最適です。
Adam Arold、2013年

4
推測の1つ:クイックソートは安定していませんが、マージソートは安定しています。プリミティブの場合、安定/非安定のソートは関係ありません。オブジェクトの場合はそうです(少なくとも、不安定なソートに対してバグが報告される可能性があります)。
パルジファル2013年

2
@ EJP、JDK作者の意図が公開されるのを妨げるものは何もありません。一旦公開されると、著者自身が答える必要はありません。実際、JDKの作成者が応答しなくても、推測以上の答えを得ることができます。
Pacerier 2014年

回答:


187

Josh Blochからの可能性が高い§

私はこれらのメソッドを作成したので、答える資格があると思います。単一の最適なソートアルゴリズムは存在しません。マージソートと比較した場合、QuickSortには2つの大きな欠点があります。

  1. 安定していません(仮説どおり)。

  2. n log nのパフォーマンスは保証されません。病理学的入力の2次パフォーマンスに低下する可能性があります。

(値)の等価性とは異なる同一性の概念がないため、安定性はプリミティブ型の問題ではありません。また、2次動作の可能性は、BentelyとMcIlroyの実装(またはその後、Dual Pivot Quicksortの実装)では実際には問題にならないと考えられたため、これらのQuickSortバリアントがプリミティブソートに使用されました。

任意のオブジェクトをソートする場合、安定性は重要です。たとえば、電子メールメッセージを表すオブジェクトがあり、それらを最初に日付で、次に送信者でソートするとします。あなたはそれらが各送信者の中で日付によってソートされることを期待していますが、それはソートが安定している場合にのみ当てはまります。これが、オブジェクト参照をソートするための安定したソート(マージソート)を提供することを選択した理由です。(厳密に言えば、複数の連続した安定したソートは、ソートとは逆の順序でキーの辞書式順序になります。最後のソートが最も重要なサブキーを決定します。)

Merge Sort が入力に関係なくn log n(時間)のパフォーマンスを保証することは良い副次的な利点です。もちろん、欠点もあります。クイックソートは「インプレース」ソートです。(コールスタックを維持するために)log n外部スペースのみが必要です。一方、マージ、ソートには、O(n)外部スペースが必要です。TimSortバリアント(Java SE 6で導入)は、入力配列がほぼソートされている場合、必要なスペース(O(k))が大幅に少なくなります。

また、以下が関連します。

オブジェクト参照をソートするためにjava.util.Arrays.sortおよびjava.util.Collections.sortによって(間接的に)使用されるアルゴリズムは、「変更されたmergesort(下位のサブリストの最上位の要素が以下の場合にマージが省略される)です。上位サブリストの最下位の要素)。」これは、O(n log n)のパフォーマンスを保証し、O(n)の追加スペースを必要とする、かなり高速で安定したソートです。その日(1997年にJoshua Blochによって書かれました)、それは良い選択でしたが、今日はもっと良くできます。

2003年以降、Pythonのリストソートはtimsortと呼ばれるアルゴリズムを使用してきました(それを書いたTim Petersの後)。これは、部分的に並べ替えられた配列で実行する場合に必要な比較がn log(n)よりはるかに少ない安定した適応型の反復マージソートであり、ランダム配列で実行する場合、従来のマージソートと同等のパフォーマンスを提供します。すべての適切なマージソートと同様に、ティムソートは安定していて、O(n log n)時間で実行されます(最悪の場合)。最悪の場合、timsortはn / 2個のオブジェクト参照のための一時的な記憶領域を必要とします。最良の場合、必要なスペースはごくわずかです。これを現在の実装と比較してください。現在の実装では、n個のオブジェクト参照に常に追加のスペースが必要であり、ほぼソートされたリストでのみn log n個を超えます。

Timsortについて詳しくは、http://svn.python.org/projects/python/trunk/Objects/listsort.txtを ご覧ください

Tim Petersの元の実装はCで記述されています。JoshuaBlochはそれをCからJavaに移植し、最終的なテスト、ベンチマーク、および広範囲にわたる調整を行いました。結果のコードは、java.util.Arrays.sortのドロップイン置換です。高度に順序付けられたデータでは、このコードは現在の実装(HotSpotサーバーVM)の最大25倍の速度で実行できます。ランダムデータでは、新旧の実装の速度は同等です。非常に短いリストの場合、新しい実装は、ランダムデータでも古い実装よりも大幅に高速です(不要なデータのコピーを回避するため)。

また、Java 7はメソッドArrays.SortにTim Sortを使用していますか?

単一の「最良の」選択はありません。他の多くのものと同様に、それはトレードオフについてです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.