Mengapa Collections.sort menggunakan Mergesort tetapi Arrays.sort tidak?

97

Saya menggunakan JDK-8 (x64). Untuk Arrays.sort(primitif) saya menemukan yang berikut ini di dokumentasi Java:

Algoritme pengurutannya adalah Dual-Pivot Quicksort oleh Vladimir Yaroslavskiy, Jon Bentley, dan Joshua Bloch.`

Untuk Collections.sort(objek) saya menemukan "Timsort" ini:

Implementasi ini adalah mergesort yang stabil, adaptif, dan berulang ... Implementasi ini membuang daftar yang ditentukan ke dalam larik, mengurutkan larik , dan mengulangi daftar yang menyetel ulang setiap elemen dari posisi yang sesuai dalam larik.

Jika Collections.sortmenggunakan array, mengapa tidak memanggil Arrays.sortatau menggunakan QuickSort pivot ganda ? Mengapa menggunakan Mergesort ?

Quest Monger
sumber
9
Itulah javadoc untuk array primitif - array Objek diurutkan menggunakan meregsort.
assylias
2
mergesort selalu memberikan u nlogn sementara quicksort kadang-kadang dapat memberikan nlogn2 ukuran array secara genetis tidak terlalu besar tetapi koleksi dengan mudah mencapai jutaan entri sehingga mengambil risiko nlogn2 tidak sebanding dengan PS nlogn2 yang saya maksudkan sqaure dari n
Kumar Saurabh
O (n ^ 2) untuk quicksort adalah kasus terburuk yang ekstrem. Dalam praktiknya lebih cepat
James Wierzba
tapi kamu tidak bisa mengabaikan caese itu saat membuat api
Kumar Saurabh
2
Tautan ini sangat terkait.
qartal

Jawaban:

100

API menjamin pengurutan stabil yang tidak ditawarkan Quicksort . Namun, saat mengurutkan nilai primitif berdasarkan urutan aslinya, Anda tidak akan melihat perbedaan karena nilai primitif tidak memiliki identitas. Oleh karena itu, Quicksort dapat digunakan untuk array primitif dan akan digunakan jika dianggap lebih efisien¹.

Untuk objek yang mungkin Anda perhatikan, ketika objek dengan identitas berbeda yang dianggap sama menurut equalsimplementasinya atau yang disediakan Comparatormengubah urutannya. Oleh karena itu, Quicksort bukanlah suatu pilihan. Jadi varian MergeSort digunakan, versi Java saat ini menggunakan TimSort . Ini berlaku untuk keduanya, Arrays.sortdan Collections.sort, meskipun dengan Java 8, Listalgoritma itu sendiri dapat menimpa algoritme pengurutan.


¹ Keuntungan efisiensi Quicksort adalah membutuhkan lebih sedikit memori saat dilakukan di tempat. Tetapi ini memiliki kinerja kasus terburuk yang dramatis dan tidak dapat mengeksploitasi proses data yang telah diurutkan sebelumnya dalam array, yang dilakukan oleh TimSort .

Oleh karena itu, algoritme pengurutan dikerjakan ulang dari versi ke versi, sambil tetap berada di kelas dengan nama yang sekarang menyesatkan DualPivotQuicksort. Selain itu, dokumentasi tidak sesuai, yang menunjukkan, bahwa secara umum adalah ide yang buruk, untuk menyebutkan algoritme yang digunakan secara internal dalam spesifikasi, jika tidak diperlukan.

Situasi saat ini (termasuk Java 8 hingga Java 11) adalah sebagai berikut:

  • Umumnya, metode pengurutan untuk array primitif hanya akan menggunakan Quicksort dalam keadaan tertentu. Untuk array yang lebih besar, mereka akan mencoba mengidentifikasi proses data yang telah diurutkan terlebih dahulu, seperti yang dilakukan TimSort , dan akan menggabungkannya ketika jumlah proses tidak melebihi ambang tertentu. Jika tidak, mereka akan kembali ke Quicksort , tetapi dengan implementasi yang akan kembali ke jenis Penyisipan untuk rentang kecil, yang tidak hanya memengaruhi larik kecil, tetapi juga rekursi pengurutan cepat.
  • sort(char[],…)dan sort(short[],…)tambahkan kasus khusus lainnya, untuk menggunakan Sortir penghitungan untuk array yang panjangnya melebihi ambang tertentu
  • Demikian juga, sort(byte[],…)akan menggunakan jenis Penghitungan , tetapi dengan ambang yang jauh lebih kecil, yang menciptakan kontras terbesar dengan dokumentasi, karena sort(byte[],…)tidak pernah menggunakan Quicksort. Ini hanya menggunakan semacam penyisipan untuk larik kecil dan jenis Penghitungan sebaliknya.
Holger
sumber
1
Hmm, yang menarik, Collections.sort Javadoc menyatakan: "Jenis ini dijamin stabil", tetapi karena didelegasikan ke List.sort, yang dapat diganti dengan implementasi daftar, penyortiran stabil tidak dapat dijamin oleh Collections.sort untuk semua daftar implementasi. Atau apakah saya melewatkan sesuatu? Dan List.sort tidak memerlukan alogirme pengurutan agar stabil.
Puce
11
@ Puce: itu berarti bahwa tanggung jawab untuk jaminan itu sekarang berada di tangan mereka yang menerapkan List.sortmetode utama . Collections.sorttidak pernah bisa menjamin kerja yang benar untuk setiap Listimplementasi karena tidak dapat menjamin, misalnya bahwa Listtidak secara palsu mengubah isinya. Itu semua intinya bahwa jaminan Collections.sorthanya berlaku untuk Listimplementasi yang benar (dan benar Comparatoratau equalsimplementasi).
Holger
1
@ Puce: Tapi Anda benar, Javadoc tidak sama eksplisitnya tentang batasan ini di kedua metode Tapi setidaknya status dokumentasi terbaru yang Collections.sortakan didelegasikan ke List.sort.
Holger
@ Puce: ada banyak sekali contoh ini, di mana properti penting bukan bagian dari tipe tetapi hanya disebutkan dalam dokumentasi (dan karenanya tidak diperiksa oleh kompilator). Sistem tipe Java terlalu lemah untuk mengekspresikan properti yang menarik. (Ini tidak jauh berbeda dari bahasa yang diketik secara dinamis dalam hal ini, di sana juga, properti didefinisikan dalam dokumentasi dan terserah pada pemrogram untuk memastikan mereka tidak dilanggar.) Lebih jauh lagi, sebenarnya: apakah Anda memperhatikan yang Collections.sortbahkan tidak menyebutkan dalam tanda tangan tipenya bahwa keluarannya diurutkan?
Jörg W Mittag
1
Dalam bahasa dengan sistem tipe yang lebih ekspresif, tipe yang dikembalikan Collections.sortakan menjadi sesuatu seperti "kumpulan dengan tipe dan panjang yang sama sebagai input dengan properti yang 1) setiap elemen yang ada dalam input juga ada dalam output, 2 ) untuk setiap pasangan elemen dari output, yang kiri tidak lebih besar dari yang kanan, 3) untuk setiap pasang elemen yang sama dari output, indeks kiri dalam input lebih kecil dari yang kanan "atau semacamnya bahwa.
Jörg W Mittag
20

Saya tidak tahu tentang dokumentasinya, tetapi implementasi java.util.Collections#sortdi Java 8 (HotSpot) berjalan seperti ini:

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

Dan List#sortimplementasi ini:

@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);
    }
}

Jadi, pada akhirnya, Collections#sortmenggunakan Arrays#sort(dari elemen objek) di belakang layar. Implementasi ini menggunakan merge sort atau tim sort.

Luiggi Mendoza
sumber
16

Menurut Javadoc, hanya array primitif yang diurutkan menggunakan Quicksort. Larik objek juga diurutkan dengan Mergesort.

Jadi Collections.sort tampaknya menggunakan algoritme pengurutan yang sama seperti Arrays.sort untuk Objek.

Pertanyaan lain adalah mengapa algoritma pengurutan yang berbeda digunakan untuk array primitif daripada untuk array Object?

Puce
sumber
2

Seperti yang dinyatakan di banyak jawaban.

Quicksort digunakan oleh Arrays.sort untuk menyortir koleksi primitif karena stabilitas tidak diperlukan (Anda tidak akan tahu atau peduli jika dua int identik ditukar dalam pengurutan)

MergeSort atau lebih spesifik Timsort digunakan oleh Arrays.sort untuk mengurutkan koleksi objek. Stabilitas diperlukan. Quicksort tidak memberikan stabilitas, Timsort menyediakannya.

Collections.sort mendelegasikan ke Arrays.sort, itulah sebabnya Anda melihat javadoc mereferensikan MergeSort.

cogitoboy.dll
sumber
1

Quick Sort memiliki dua kelemahan utama dalam hal merge sort:

  • Itu tidak stabil jika menyangkut non primitif.
  • Itu tidak menjamin kinerja n log n.

Stabilitas bukanlah masalah untuk tipe primitif, karena tidak ada gagasan tentang identitas yang berbeda dari persamaan (nilai).

Stabilitas adalah masalah besar saat menyortir objek arbitrer. Ini adalah keuntungan sampingan yang bagus bahwa Merge Sort menjamin n log n (waktu) kinerja apapun inputnya. Itulah mengapa merge sort dipilih untuk menyediakan pengurutan yang stabil (Merge Sort) untuk mengurutkan referensi objek.

Krutik
sumber
1
Apa maksudmu "Tidak stabil"?
Arun Gowda