Perché il metodo Arrays.sort di Java utilizza due diversi algoritmi di ordinamento per tipi diversi?


121

Il Arrays.sortmetodo di Java 6 utilizza Quicksort per array di primitive e merge sort per array di oggetti. Credo che la maggior parte delle volte Quicksort sia più veloce di merge sort e costi meno memoria. I miei esperimenti lo supportano, sebbene entrambi gli algoritmi siano O (n log (n)). Allora perché vengono utilizzati algoritmi diversi per tipi diversi?


14
Il caso peggiore di Quicksort è N ^ 2 non NlogN.
codaddict

Aspetta, cosa succede se hai un array di Integers o qualcosa del genere?
Tikhon Jelvis

1
Non è spiegato nella fonte che hai letto?
Humphrey Bogart,

5
Questa informazione non è più attuale. A partire da Java SE 7, MergeSort è stato sostituito con TimSort e QuickSort è stato sostituito con Dual-Pivot QuickSort . Vedi la mia risposta di seguito per i collegamenti ai documenti dell'API Java.
Will Byrne

Risposte:


200

La ragione più probabile: quicksort non è stabile , cioè voci uguali possono cambiare la loro posizione relativa durante l'ordinamento; tra le altre cose, questo significa che se si ordina un array già ordinato, potrebbe non rimanere invariato.

Poiché i tipi primitivi non hanno identità (non c'è modo di distinguere due interi con lo stesso valore), questo non ha importanza per loro. Ma per i tipi di riferimento, potrebbe causare problemi per alcune applicazioni. Pertanto, per questi viene utilizzato un merge sort stabile.

OTOH, un motivo per non utilizzare l'ordinamento di unione stabile (garantito n * log (n)) per i tipi primitivi potrebbe essere che richiede la creazione di un clone dell'array. Per i tipi di riferimento, in cui gli oggetti indicati di solito occupano molta più memoria rispetto alla matrice di riferimenti, questo in genere non ha importanza. Ma per i tipi primitivi, la clonazione dell'array raddoppia completamente l'utilizzo della memoria.


1
Un altro motivo per utilizzare Quicksort è che nel caso medio, Quicksort è più veloce di Mergesort. Sebbene quicksort effettui più confronti rispetto al mergesort, fa molto meno accessi agli array. Il quicksort a 3 vie può anche ottenere tempo lineare se l'input contiene molte voci duplicate, cosa non insolita nelle applicazioni pratiche (la mia ipotesi è che anche l'ordinamento rapido a doppio perno abbia questa proprietà).
Jingguo Yao

Per i tipi primitivi non clona l'array, può ordinarli in posizione, quindi penso che l'unica ragione sia il contratto di stabilità, fondamentalmente ...
rogerdpack

27

Secondo i documenti dell'API Java 7 citati in questa risposta , Arrays#Sort()per gli array di oggetti ora utilizza TimSort , che è un ibrido di MergeSort e InsertionSort. D'altra parte, Arrays#sort()per gli array primitivi ora utilizza Dual-Pivot QuickSort . Queste modifiche sono state implementate a partire da Java SE 7.


2
Non è una risposta, perché sono stati scelti 2 diversi algoritmi.
Alexandr

12

Una ragione a cui posso pensare è che quicksort ha una complessità temporale del caso peggiore di O ( n ^ 2 ) mentre il mergesort conserva il tempo del caso peggiore di O ( n log n ). Per gli array di oggetti c'è una buona aspettativa che ci saranno più riferimenti a oggetti duplicati, che è un caso in cui quicksort fa peggio.

Esiste un discreto confronto visivo di vari algoritmi , prestare particolare attenzione al grafico più a destra per diversi algoritmi.


2
Il java quicksort è un quicksort modificato che non derade a O (n ^ 2), dalla documentazione "Questo algoritmo offre prestazioni n * log (n) su molti set di dati che fanno degradare altri quicksort a prestazioni quadratiche"
sbridges

7

Stavo frequentando il corso Coursera sugli algoritmi e in una delle lezioni il professor Bob Sedgewick menzionava la valutazione per l'ordinamento del sistema Java:

"Se un programmatore utilizza oggetti, forse lo spazio non è una considerazione di importanza critica e lo spazio aggiuntivo utilizzato da un merge sort forse non è un problema. E se un programmatore utilizza tipi primitivi, forse le prestazioni sono la cosa più importante, quindi usano ordinamento veloce. "


4
Non è il motivo principale. Subito dopo quella frase c'era una domanda, incorporata nel video, su "Perché per i tipi di riferimento viene utilizzato MergeSort?" (perché è stabile). Penso che Sedgewick non l'abbia menzionato nel video per lasciarlo in discussione.
come il

1

java.util.Arrays utilizza quicksort per tipi primitivi come int e mergesort per oggetti che implementano Comparable o utilizzano un Comparator . L'idea di usare due metodi differenti è che se un programmatore usa oggetti forse lo spazio non è una considerazione di importanza critica e quindi lo spazio extra usato da mergesort forse non è un problema e se il programmatore usa tipi primitivi forse le prestazioni sono la cosa più importante quindi usa il quicksort .

Ad esempio: questo è l'esempio in cui l'ordinamento è importante.

inserisci qui la descrizione dell'immagine

Ecco perché gli ordinamenti stabili hanno senso per i tipi di oggetto, in particolare i tipi di oggetto modificabili e i tipi di oggetto con più dati oltre alla chiave di ordinamento, e il mergesort è un tale ordinamento. Ma per i tipi primitivi la stabilità non è solo irrilevante. Non ha senso.

Fonte: INFO


0

Il Arrays.sortmetodo di Java utilizza quicksort, insertion sort e mergesort. C'è anche un quicksort singolo e doppio pivot implementato nel codice OpenJDK. L'algoritmo di ordinamento più veloce dipende dalle circostanze ei vincitori sono: l'ordinamento per inserzione per piccoli array (47 attualmente scelti), mergesort per gli array per lo più ordinati e quicksort per gli array rimanenti, quindi Array.sort () di Java cerca di scegliere l'algoritmo migliore per applicare in base a tali criteri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.