Neden Collections.sort Mergesort kullanıyor ama Arrays.sort kullanmıyor?


98

JDK-8 (x64) kullanıyorum. İçin Arrays.sort(ilkel) Java belgelerinde aşağıdaki bulundu:

Sıralama algoritması, Vladimir Yaroslavskiy, Jon Bentley ve Joshua Bloch imzalı Dual-Pivot Quicksort'tur .

İçin Collections.sort(nesneler) Bu "Timsort" bulundu:

Bu uygulama kararlı, uyarlanabilir, yinelemeli bir birleştirmedir ... Bu uygulama , belirtilen listeyi bir diziye döker, diziyi sıralar ve her bir öğeyi dizideki karşılık gelen konumdan sıfırlayarak liste üzerinde yineler.

Bir Collections.sortdizi kullanılıyorsa, neden sadece Arrays.sortdual-pivot QuickSort'u çağırmıyor veya kullanmıyor ? Neden Mergesort kullanmalı ?


9
Bu, ilkel diziler için javadoc - Nesnelerin dizileri meregsort kullanılarak sıralanır.
assylias

2
mergesort verir u quicksort bazen vermek nlogn2 geneally diziler boyut büyük ama koleksiyonları kolayca nlogn2 riski alarak bu yüzden girişlerinin milyonlarca kadar gider değil olabilir iken daima nlogn i n sqaure anlamına nlogn2 değerinde PS değil
Kumar Saurabh

Quicksort için O (n ^ 2) aşırı kötü durumdur. Pratikte daha hızlı
James Wierzba

ama api yaparken bu caese'i görmezden gelemezsiniz
Kumar Saurabh

2
Bu bağlantı çok alakalı.
qartal

Yanıtlar:


100

API , Quicksort'un sunmadığı istikrarlı bir sıralamayı garanti eder . Ancak, ilkel değerleri doğal sıralarına göre sıralarken, ilkel değerlerin kimliği olmadığından bir fark görmezsiniz. Bu nedenle, Quicksort ilkel diziler için kullanılabilir ve daha verimli olduğu düşünüldüğünde kullanılacaktır¹.

Nesneler için, equalsuygulanmalarına veya sağlananlara göre eşit kabul edilen farklı kimliğe sahip nesnelerin Comparatorsırasını değiştirdiğini fark edebilirsiniz. Bu nedenle, Quicksort bir seçenek değildir. Dolayısıyla, MergeSort'un bir çeşidi kullanılır, mevcut Java sürümleri TimSort'u kullanır . Bu her ikisi için de geçerlidir Arrays.sortve Collections.sortJava 8 ile birlikte, Listkendisi sıralama algoritmalarını geçersiz kılabilir.


¹ Quicksort'un verimlilik avantajı, yerinde yapıldığında daha az belleğe ihtiyaç duymasıdır . Ancak dramatik bir en kötü durum performansına sahiptir ve TimSort'un yaptığı gibi bir dizideki önceden sıralanmış verilerin çalıştırılmasından yararlanamaz .

Bu nedenle, artık yanıltıcı olarak adlandırılan sınıfta kalırken, sıralama algoritmaları sürümden sürüme yeniden çalışıldı DualPivotQuicksort. Ayrıca, dokümantasyon uyuşmadı, bu da genel olarak, gerekli olmadığında bir spesifikasyonda dahili olarak kullanılan bir algoritmayı adlandırmanın kötü bir fikir olduğunu gösteriyor.

Mevcut durum (Java 8 ila Java 11 dahil) aşağıdaki gibidir:

  • Genel olarak, ilkel diziler için sıralama yöntemleri, yalnızca belirli koşullar altında Quicksort'u kullanır . Daha büyük diziler için, TimSort'un yaptığı gibi, önce önceden sıralanmış veri çalıştırmalarını belirlemeye çalışacaklar ve çalıştırma sayısı belirli bir eşiği aşmadığında bunları birleştirecekler. Aksi takdirde, Quicksort'a geri dönerler , ancak küçük aralıklar için Insertion sıralamasına geri dönecek bir uygulama ile , bu sadece küçük dizileri değil aynı zamanda hızlı sıralamanın özyinelemesini de etkiler.
  • sort(char[],…)ve uzunluğu belirli bir eşiği aşan diziler için Sayma sıralamasınısort(short[],…) kullanmak için başka bir özel durum ekleyin
  • Aynı şekilde, Sayma sıralamasısort(byte[],…) kullanacak , ancak çok daha küçük bir eşikle, hiçbir zaman Quicksort kullanmadığı için dokümantasyonla en büyük kontrastı yaratıyor . Yalnızca küçük diziler için Ekleme sıralaması , aksi halde Sayma sıralaması kullanır .sort(byte[],…)

1
Hmm, ilginç bir şekilde Collections.sort Javadoc şunu belirtir: "Bu sıralamanın kararlı olması garanti edilir", ancak liste uygulamaları tarafından geçersiz kılınabilen List.sort'a delege ettiği için, kararlı sıralama gerçekten tüm liste için Collections.sort tarafından garantiye alınamaz uygulamalar. Yoksa bir şeyi mi özledim? Ve List.sort, sıralama düzeninin kararlı olmasını gerektirmez.
Puce

11
@Puce: Bu basitçe, bu garantinin sorumluluğunun artık geçersiz kılma List.sortyöntemini uygulayanların elinde olduğu anlamına gelir . Collections.sorther Listuygulama için doğru çalışmayı asla garanti edemez, örneğin Listiçeriğini sahte bir şekilde değiştirmez. Tüm bunlar, garantinin Collections.sortyalnızca doğru Listuygulamalar (ve doğru uygulamalar Comparatorveya equalsuygulamalar) için geçerli olduğu anlamına gelir.
Holger

1
@Puce: Ama haklısın, Javadoc her iki yöntemde de bu kısıtlama konusunda eşit derecede açık değil Ama en azından en son dokümantasyon durumları Collections.sortdelege edecek List.sort.
Holger

@Puce: Bunun tonlarca örneği var, burada önemli özellikler türün bir parçası değil, yalnızca belgelerde bahsediliyor (ve bu nedenle derleyici tarafından kontrol edilmiyor). Java'nın tür sistemi, herhangi bir ilginç özelliği ifade edemeyecek kadar zayıftır. (Bu bakımdan dinamik olarak yazılmış bir dilden çok da farklı değil, orada da özellikler belgelerde tanımlanmıştır ve ihlal edilmediklerinden emin olmak programcıya kalmıştır.) Aslında daha da ileri gidiyor: fark ettiniz mi? bu Collections.sort, tip imzasında çıktının sıralandığından bahsetmiyor mu?
Jörg W Mittag

1
Daha açıklayıcı bir tür sistemine sahip bir dilde, dönüş türü Collections.sort"girdiyle aynı tür ve uzunluktaki bir koleksiyonun şu özelliklere sahip olması" gibi bir şey olacaktır: 1) girdide bulunan her öğenin çıktıda da mevcut olması, 2 ) çıktıdaki her eleman çifti için, soldaki sağ olandan büyük değildir, 3) çıktıdaki her eşit eleman çifti için, girdideki solun indeksi sağdakinden daha küçüktür "veya benzeri bu.
Jörg W Mittag

20

Belgeleri bilmiyorum, ancak java.util.Collections#sortJava 8'de (HotSpot) uygulanması şu şekildedir:

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

Ve List#sortşu uygulamaya sahiptir:

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

Sonuç olarak, perde arkasında (nesne öğelerinin) Collections#sortkullanır Arrays#sort. Bu uygulama, birleştirme sıralaması veya zaman sıralaması kullanır.


16

Javadoc'a göre, Quicksort kullanılarak yalnızca ilkel diziler sıralanır. Nesne dizileri de bir Mergesort ile sıralanır.

Bu yüzden Collections.sort, Nesneler için Arrays.sort ile aynı sıralama algoritmasını kullanıyor gibi görünüyor.

Başka bir soru, ilkel diziler için neden Nesne dizilerinden farklı bir sıralama algoritmasının kullanıldığı olabilir?


2

Cevapların çoğunda belirtildiği gibi.

Quicksort, Arrays.sort tarafından ilkel koleksiyonları sıralamak için kullanılır, çünkü kararlılık gerekli değildir (sıralamada iki özdeş girişin takas edilip edilmediğini bilemez veya umursamazsınız)

MergeSort veya daha spesifik olarak Timsort, Arrays.sort tarafından nesne koleksiyonlarını sıralamak için kullanılır. Kararlılık gereklidir. Quicksort istikrar sağlamaz, Timsort sağlar.

Collections.sort delegeleri Arrays.sort'a, bu nedenle MergeSort'a referans veren javadoc'u görüyorsunuz.


1

Hızlı Sıralama, sıralamayı birleştirme söz konusu olduğunda iki büyük dezavantaja sahiptir:

  • İlkel olmayana gelince kararlı değil.
  • N log n performansını garanti etmez.

Kararlılık, (değer) eşitliğinden farklı bir kimlik kavramı olmadığından, ilkel tipler için bir sorun değildir.

İstikrar, rastgele nesneleri sıralarken çok önemlidir. Birleştirme Sıralamanın, girdi ne olursa olsun n log n (zaman) performansını garanti etmesi güzel bir yan avantajıdır. Bu nedenle, nesne referanslarını sıralamak için kararlı bir sıralama (Birleştirme Sıralaması) sağlamak için birleştirme sıralaması seçilir.


1
"Kararlı değil" ne demek?
Arun Gowda
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.