Чому метод Arrays.sort в Java використовує два різні алгоритми сортування для різних типів?


121

Arrays.sortМетод Java 6 використовує Quicksort для масивів примітивів і сортування об'єднань для масивів об'єктів. Я вважаю, що більшість часу Quicksort швидше, ніж сортування об'єктів і коштує менше пам'яті. Мої експерименти це підтверджують, хоча обидва алгоритми є O (n log (n)). То чому для різних типів використовуються різні алгоритми?


14
Найгірший випадок для Quicksort - N ^ 2, а не NlogN.
кодифікація

Зачекайте, що станеться, якщо у вас є масив Integers чи щось таке?
Тихон Єлвіс

1
Чи не пояснено це в прочитаному джерелі?
Хамфрі Богарт

5
Ця інформація більше не є актуальною. Починаючи з Java SE 7, MergeSort був замінений на TimSort, а QuickSort був замінений на Dual-Pivot QuickSort . Дивіться мою відповідь нижче щодо посилань на документи Java API.
Буде Бірн

Відповіді:


200

Найбільш ймовірна причина: швидке сортування НЕ стійка , тобто рівні елементи можуть змінити своє відносне положення під час сортування; серед іншого, це означає, що якщо ви сортуєте вже відсортований масив, він може не залишитися незмінним.

Оскільки примітивні типи не мають ідентичності (немає можливості відрізнити два входи з однаковим значенням), для них це не має значення. Але для довідкових типів це може спричинити проблеми для деяких програм. Тому для них використовується стабільний сорт злиття.

OTOH, причиною не використовувати (гарантований n * log (n)) стабільний сорт злиття для примітивних типів може бути те, що для цього потрібен клон масиву. Для типів посилань, де згадані об'єкти зазвичай займають набагато більше пам'яті, ніж масив посилань, це, як правило, не має значення. Але для примітивних типів клонування масиву прямо вдвічі збільшує використання пам'яті.


1
Ще одна причина використання quicksort - це те, що в середньому випадку, quicksort швидше, ніж mergesort. Хоча quicksort робить більше порівнянь, ніж злиття, він робить набагато менший доступ до масиву. Трехсторонній швидкодіючий вибір також може досягти лінійного часу, якщо вхід містить багато дублюваних записів, що не є незвичним для практичних застосувань (я здогадуюсь, що двосхилий швидкий сортування також має цю властивість).
Jingguo Yao

Для примітивних типів він не клонує масив, він може сортувати їх за місцем, тому я думаю, що єдиною причиною є контракт на стабільність, в основному ...
rogerdpack

27

Згідно з документами API Java 7, цитованими у цій відповіді , Arrays#Sort()для об’єктних масивів зараз використовується TimSort , який є гібридом MergeSort та InsertionSort. З іншого боку, Arrays#sort()для примітивних масивів зараз використовується Dual-Pivot QuickSort . Ці зміни були здійснені, починаючи з Java SE 7.


2
Це не відповідь, чому було обрано 2 різні алгоритми.
Олександр

12

Однією з причин, про яку я можу подумати, є те, що у quicksort є найгірша часова складність у випадку O ( n ^ 2 ), тоді як злиття зберігає найгірший час O ( n log n ). Для масивів об'єктів існує справедливе сподівання, що буде декілька повторюваних посилань на об'єкти, що є одним із випадків, коли quicksort робить найгірше.

Існує гідне візуальне порівняння різних алгоритмів , приділіть особливу увагу правильному графіку для різних алгоритмів.


2
Java quicksort - це модифікований кікспорт, який не схиляється до O (n ^ 2), з документів "Цей алгоритм пропонує n * log (n) продуктивність на багатьох наборах даних, які призводять до того, що інші quicksorts деградують до квадратичної продуктивності"
sbridges

7

Я брав уроки Coursera з алгоритмів, і в одній з лекцій професор Боб Седжевік згадував оцінку для сортування системи Java:

"Якщо програміст використовує об'єкти, можливо, простір не є критично важливим фактором, а додатковий простір, який використовується сортуванням злиття, може бути не проблемою. швидкий сорт ».


4
Це не головна причина. Відразу після цього речення виникло запитання, вбудоване у відео про "Чому для референтних типів використовується MergeSort?" (бо стабільний). Я думаю, що Седжевік не згадував це у відео, щоб залишити це питання.
likern

1

java.util.Arrays використовує QuickSort для примітивних типів , таких як Int і злиття для об'єктів , які реалізують Порівнянними або використовувати компаратор . Ідея використання двох різних методів полягає в тому, що якщо програміст використовує об'єкти, можливо, простір не є критично важливим фактором, і тому додатковий простір, який використовує mergesort, можливо, не є проблемою, і якщо програміст використовує примітивні типи, можливо, продуктивність є найважливішою справою, тому використовуйте швидке сортування .

Наприклад: Це приклад, коли сортування стабільності має значення.

введіть тут опис зображення

Ось чому стабільні сорти мають сенс для типів об'єктів, особливо змінних типів об'єктів та типів об'єктів, у яких більше даних, ніж просто ключ сортування, і mergesort є таким видом. Але для примітивних типів стабільність не тільки не має значення. Це безглуздо.

Джерело: INFO


0

Arrays.sortМетод Java використовує quicksort, сортування вставки та злиття. Існує навіть однократний і подвійний стрижневий швидкості, реалізований у коді OpenJDK. Найбільш швидкий алгоритм сортування залежить від обставин, і переможцями є: сортування вставки для малих масивів (47 обраних на даний момент), об'єднання об'єднаних масивів для більшості сортів, а також для швидкості сортування масивів для інших, тому Java Array.sort () намагається вибрати найкращий алгоритм для застосовувати на основі цих критеріїв.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.