JavaのArrays.sortメソッドは、異なる型に対して2つの異なるソートアルゴリズムを使用するのはなぜですか?


121

Java 6のArrays.sortメソッドは、プリミティブの配列にはQuicksortを使用し、オブジェクトの配列にはマージソートを使用します。ほとんどの場合、クイックソートはマージソートよりも高速で、メモリの消費量も少ないと思います。私の実験は、両方のアルゴリズムがO(n log(n))ですが、それをサポートしています。では、なぜ異なるタイプに異なるアルゴリズムが使用されるのでしょうか?


14
クイックソートの最悪のケースはN ^ NではなくN ^ 2です。
codaddict

ちょっと待ってください。sの配列などがあるとどうなりますIntegerか?
Tikhon Jelvis

1
これはあなたが読んだソースで説明さていませんか?
ハンフリーボガート

5
この情報は最新ではありません。Java SE 7以降、MergeSortはTimSortに、QuickSortはDual-Pivot QuickSortに置き換えられました。Java APIドキュメントへのリンクについては、以下の私の回答を参照してください。
ウィルバーン

回答:


200

最も可能性の高い理由:クイックソートは安定していません。つまり、等しいエントリはソート中に相対的な位置を変更できます。特に、これは、既にソートされた配列をソートした場合、変更されないままになる可能性があることを意味します。

プリミティブ型にはIDがないため(同じ値を持つ2つのintを区別する方法はありません)、これはそれらにとって重要ではありません。ただし、参照型の場合、一部のアプリケーションで問題が発生する可能性があります。したがって、それらには安定したマージソートが使用されます。

OTOH、プリミティブ型に(保証されたn * log(n))安定したマージソートを使用しない理由は、配列のクローンを作成する必要があるためです。参照タイプの場合、参照されるオブジェクトは通常、参照の配列よりもはるかに多くのメモリを消費しますが、これは通常は問題になりません。ただし、プリミティブ型の場合、配列を完全に複製すると、メモリ使用量が2倍になります。


1
クイックソートを使用するもう1つの理由は、平均的なケースでは、クイックソートがマージソートよりも高速であることです。クイックソートはマージソートより多くの比較を行いますが、配列アクセスははるかに少なくなります。3ウェイクイックソートは、入力に重複したエントリが多数含まれている場合にも線形時間を実現できます。これは、実際のアプリケーションでは珍しいことではありません(デュアルピボットクイックソートにもこのプロパティがあると思います)。
Jingguo Yao

プリミティブ型の場合、配列のクローンを作成せず、それらを所定の位置に並べ替えることができるので、基本的には安定性のコントラクトが唯一の理由だと思います
rogerdpack

27

で引用したJava 7 APIドキュメントによれば、この回答Arrays#Sort()オブジェクトのアレイで現在使用TimSortマージと挿入ソートのハイブリッドです。一方、Arrays#sort()プリミティブ配列では、Dual-Pivot QuickSortを使用するようになりました。これらの変更は、Java SE 7から実装されました。


2
2つの異なるアルゴリズムが選択された理由は答えではありません。
Alexandr

12

私が考えることができる1つの理由は、クイックソートがO(n ^ 2)の最悪の場合の時間の複雑さを持っているのに対し、マージソートはO(n log n)の最悪の場合の時間を保持することです。オブジェクト配列の場合、クイックソートが最悪の場合の1つのケースである、複数のオブジェクト参照が重複することが予想されます。

さまざまなアルゴリズムの適切な視覚的比較があります。さまざまなアルゴリズムの右端のグラフに特に注意してください。


2
Javaクイックソートは、O(n ^ 2)に格下げしない変更されたクイックソートであり、「このアルゴリズムは、他のクイックソートを2次のパフォーマンスに低下させる多くのデータセットでn * log(n)のパフォーマンスを提供します」
sbridges

7

私はアルゴリズムに関するコースラクラスを受講しており、講義の1つであるボブセジウィック教授はJavaシステムの評価について言及しています。

「プログラマーがオブジェクトを使用している場合、おそらくスペースは非常に重要な考慮事項ではなく、マージソートで使用される余分なスペースは問題ではない可能性があります。プログラマーがプリミティブ型を使用している場合は、パフォーマンスが最も重要であるため、クイックソート。」


4
それが主な理由ではありません。その文の直後に、「なぜ参照型にMergeSortが使用されるのか」についての質問がビデオに埋め込まれました。(安定しているため)。セッジウィックはそれを質問に残すためにビデオでそれについて言及しなかったと思います。
2015

1

java.util.Arraysの用途はクイックソート intやなどのプリミティブ型のためにマージソート実装するオブジェクトのための同等または使用Aを Comparatorをます。2つの異なる方法を使用するアイデアは、プログラマーがオブジェクトを使用している可能性がある場合、スペースはそれほど重要な考慮事項ではないため、mergesortが使用する余分なスペースは問題ではない可能性があり、プログラマーがプリミティブ型を使用している場合は、パフォーマンスが最も重要であるため、クイックソート

例:これは、ソートの安定性が重要な場合の例です。

ここに画像の説明を入力してください

そのため、オブジェクトタイプ、特に変更可能なオブジェクトタイプと、ソートキーよりも多くのデータを持つオブジェクトタイプに対して、安定したソートが有効であり、mergesortはそのようなソートです。しかし、プリミティブ型の場合、安定性は無関係なだけではありません。意味がありません。

出典:INFO


0

JavaのArrays.sortメソッドは、クイックソート、挿入ソート、およびマージソートを使用します。OpenJDKコードには、シングルとデュアルの両方のピボットクイックソートも実装されています。最速のソートアルゴリズムは状況に応じて異なり、勝者は次のとおりです。小さな配列の挿入ソート(現在選択されている47)、ほとんどのソートされた配列のマージソート、残りの配列のクイックソートなので、JavaのArray.sort()は最適なアルゴリズムを選択しようとしますそれらの基準に基づいて適用します。

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