Używam JDK-8 (x64). W przypadku Arrays.sort
(prymitywów) w dokumentacji Java znalazłem:
Algorytm sortowania to Dual-Pivot Quicksort autorstwa Vladimira Yaroslavskiya, Jona Bentleya i Joshua Blocha. "
Dla Collections.sort
(obiektów) znalazłem to „Timsort”:
Ta implementacja jest stabilnym, adaptacyjnym, iteracyjnym łączeniem ... Ta implementacja zrzuca określoną listę do tablicy, sortuje tablicę i wykonuje iterację po liście resetując każdy element z odpowiedniej pozycji w tablicy.
Jeśli Collections.sort
używa tablicy, dlaczego po prostu nie wywołuje Arrays.sort
lub nie używa funkcji QuickSort z dwoma obrotami ? Dlaczego warto korzystać z Mergesort ?
Odpowiedzi:
API gwarantuje stabilne sortowanie, którego nie oferuje Quicksort . Jednak podczas sortowania wartości pierwotnych według ich naturalnej kolejności nie zauważysz różnicy, ponieważ wartości pierwotne nie mają tożsamości. Dlatego też Quicksort może być używany dla tablic pierwotnych i będzie używany, gdy zostanie uznany za bardziej wydajny¹ .
W przypadku obiektów można zauważyć, że obiekty o różnej tożsamości, które są uważane za równe w zależności od ich
equals
wykonania lub dostarczonejComparator
zmiany, zmieniają kolejność. Dlatego Quicksort nie wchodzi w grę. Tak więc używany jest wariant MergeSort , obecne wersje Java używają TimSort . Dotyczy to obuArrays.sort
iCollections.sort
chociaż w Javie 8List
samo w sobie może przesłonić algorytmy sortowania.¹ Zaletą wydajności Quicksort jest to, że wymaga mniej pamięci, gdy jest wykonywana na miejscu. Ale ma dramatyczną wydajność w najgorszym przypadku i nie może wykorzystywać serii wstępnie posortowanych danych w tablicy, co robi TimSort .
Dlatego algorytmy sortowania zostały przerobione z wersji na wersję, pozostając w myląco nazwanej klasie
DualPivotQuicksort
. Ponadto dokumentacja nie nadrobiła zaległości, co pokazuje, że generalnie złym pomysłem jest nazwanie w specyfikacji algorytmu używanego wewnętrznie, gdy nie jest to konieczne.Obecna sytuacja (w tym Java 8 do Java 11) przedstawia się następująco:
sort(char[],…)
isort(short[],…)
dodaj kolejny przypadek specjalny, aby użyć sortowania zliczającego dla tablic, których długość przekracza określony prógsort(byte[],…)
użyje sortowania zliczającego , ale ze znacznie mniejszym progiem, co tworzy największy kontrast w stosunku do dokumentacji, ponieważsort(byte[],…)
nigdy nie używa Quicksort. Używa sortowania przez wstawianie tylko dla małych tablic i sortowania zliczania w przeciwnym razie.źródło
List.sort
metodę nadrzędną .Collections.sort
nigdy nie mógł zagwarantować poprawnego działania dla każdejList
implementacji, ponieważ nie może zagwarantować, np. żeList
nie zmienia ona błędnie swojej zawartości. Wszystko sprowadza się do tego, że gwarancjaCollections.sort
dotyczy tylko poprawnychList
implementacji (i poprawnychComparator
lubequals
wdrożeń).Collections.sort
zostanie to delegowaneList.sort
.Collections.sort
nawet nie wspomina w swoim sygnaturze typu, że dane wyjściowe są posortowane?Collections.sort
byłby czymś w rodzaju „kolekcji tego samego typu i długości co dane wejściowe, z właściwościami, że 1) każdy element obecny na wejściu jest również obecny w danych wyjściowych, 2 ) dla każdej pary elementów z wyjścia lewy jest nie większy niż prawy, 3) dla każdej pary równych elementów z wyjścia, lewy indeks na wejściu jest mniejszy niż prawy "lub coś podobnego że.Nie znam dokumentacji, ale implementacja
java.util.Collections#sort
w Javie 8 (HotSpot) wygląda tak:@SuppressWarnings({"unchecked", "rawtypes"}) public static <T> void sort(List<T> list, Comparator<? super T> c) { list.sort(c); }
I
List#sort
ma tę implementację:@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); } }
W końcu
Collections#sort
używaArrays#sort
(elementów obiektów) za kulisami. Ta implementacja używa sortowania przez scalanie lub sortowania według czasu.źródło
Według Javadoc tylko prymitywne tablice są sortowane za pomocą Quicksort. Tablice obiektów są również sortowane za pomocą Mergesort.
Wydaje się więc, że Collections.sort używa tego samego algorytmu sortowania, co Arrays.sort dla obiektów.
Innym pytaniem byłoby, dlaczego inny algorytm sortowania jest używany dla tablic pierwotnych niż dla tablic Object?
źródło
Jak stwierdzono w wielu odpowiedziach.
Funkcja Quicksort jest używana przez Arrays.sort do sortowania kolekcji pierwotnych, ponieważ stabilność nie jest wymagana (nie będziesz wiedzieć lub przejmować się tym, czy podczas sortowania zamieniono dwa identyczne liczby całkowite)
MergeSort, a dokładniej Timsort, jest używany przez Arrays.sort do sortowania kolekcji obiektów. Wymagana jest stabilność. Quicksort nie zapewnia stabilności, Timsort tak.
Collections.sort deleguje do Arrays.sort, dlatego widzisz javadoc odwołujący się do MergeSort.
źródło
Szybkie sortowanie ma dwie główne wady, jeśli chodzi o sortowanie przez scalanie:
Stabilność nie jest problemem dla typów pierwotnych, ponieważ nie istnieje pojęcie tożsamości jako odrębnej od równości (wartości).
Stabilność to poważna sprawa podczas sortowania dowolnych obiektów. Dodatkową zaletą jest to, że sortowanie przez scalanie gwarantuje wydajność n log n (czas) bez względu na dane wejściowe. Dlatego wybrano sortowanie przez scalanie, aby zapewnić stabilne sortowanie (sortowanie przez scalanie) do sortowania odniesień do obiektów.
źródło