Por que o método Arrays.sort do Java usa dois algoritmos de classificação diferentes para tipos diferentes?

121

O Arrays.sortmétodo do Java 6 usa Quicksort para arrays de primitivos e merge sort para arrays de objetos. Eu acredito que na maioria das vezes o Quicksort é mais rápido do que merge sort e custa menos memória. Meus experimentos confirmam isso, embora ambos os algoritmos sejam O (n log (n)). Então, por que algoritmos diferentes são usados ​​para tipos diferentes?

zjffdu
fonte
14
O pior caso do Quicksort é N ^ 2, não NlogN.
codaddict de
Espere, o que acontece se você tiver uma matriz de Integers ou algo assim?
Tikhon Jelvis de
1
Isso não está explicado na fonte que você leu?
Humphrey Bogart,
5
Esta informação não é mais atual. A partir do Java SE 7, MergeSort foi substituído por TimSort e QuickSort foi substituído por Dual-Pivot QuickSort . Veja minha resposta abaixo para links para os documentos da API Java.
Will Byrne
Consulte também stackoverflow.com/questions/15154158/… e para JDK 7 + consulte stackoverflow.com/questions/32334319/…
rogerdpack

Respostas:

200

O motivo mais provável: o quicksort não é estável , ou seja, entradas iguais podem mudar sua posição relativa durante a classificação; entre outras coisas, isso significa que se você classificar uma matriz já classificada, ela pode não permanecer inalterada.

Como os tipos primitivos não têm identidade (não há como distinguir dois ints com o mesmo valor), isso não importa para eles. Mas, para tipos de referência, isso pode causar problemas para alguns aplicativos. Portanto, uma classificação de mesclagem estável é usada para eles.

OTOH, uma razão para não usar a classificação de mesclagem estável (garantida n * log (n)) para tipos primitivos pode ser que ela requer a criação de um clone da matriz. Para os tipos de referência, onde os objetos referidos geralmente ocupam muito mais memória do que o array de referências, isso geralmente não importa. Mas para tipos primitivos, clonar o array dobra o uso de memória.

Michael Borgwardt
fonte
1
Outra razão para usar o quicksort é que, em geral, o quicksort é mais rápido do que o mergesort. Embora quicksort faça mais comparações do que mergesort, ele faz muito menos acessos de array. A classificação rápida de 3 vias também pode atingir o tempo linear se a entrada contiver muitas entradas duplicadas, o que não é incomum em aplicações práticas (meu palpite é que a classificação rápida de pivô duplo também tem essa propriedade).
Jingguo Yao
Para tipos primitivos, ele não clona o array, ele pode classificá-los no lugar, então acho que a única razão é o contrato de estabilidade, basicamente ...
rogerdpack
27

De acordo com os documentos da API Java 7 citados nesta resposta , Arrays#Sort()para matrizes de objetos agora usa TimSort , que é um híbrido de MergeSort e InsertionSort. Por outro lado, Arrays#sort()para matrizes primitivas agora usa Dual-Pivot QuickSort . Essas mudanças foram implementadas a partir do Java SE 7.

Will Byrne
fonte
2
Não é uma resposta, porque 2 algoritmos diferentes foram escolhidos.
Alexandr
12

Uma razão que posso pensar é que quicksort tem um pior caso de complexidade de tempo de O ( n ^ 2 ), enquanto mergesort retém o pior caso de tempo de O ( n log n ). Para matrizes de objetos, há uma expectativa razoável de que haverá várias referências de objeto duplicadas, o que é um caso em que o quicksort tem pior desempenho.

Há uma comparação visual decente de vários algoritmos , preste atenção especial ao gráfico mais à direita para diferentes algoritmos.

msw
fonte
2
O java quicksort é um quicksort modificado que não derade para O (n ^ 2), da documentação "Este algoritmo oferece n * log (n) desempenho em muitos conjuntos de dados que fazem com que outros quicksorts degradem para desempenho quadrático"
sbridges
7

Eu estava fazendo a aula do Coursera sobre Algoritmos e em uma das palestras o Professor Bob Sedgewick mencionou a avaliação para o sistema Java sort:

"Se um programador estiver usando objetos, talvez o espaço não seja uma consideração criticamente importante e o espaço extra usado por uma classificação por mesclagem talvez não seja um problema. E se um programador estiver usando tipos primitivos, talvez o desempenho seja a coisa mais importante, então eles usam ordenação rápida."

kukido
fonte
4
Não é o motivo principal. Logo após essa frase, houve uma pergunta, embutida no vídeo sobre "Por que para tipos de referência é usado MergeSort?" (porque é estável). Acho que Sedgewick não mencionou isso no vídeo para deixar em dúvida.
como em
1

java.util.Arrays usa quicksort para tipos primitivos como int e mergesort para objetos que implementam Comparable ou usam um Comparator . A ideia de usar dois métodos diferentes é que se um programador está usando objetos talvez o espaço não seja uma consideração criticamente importante e então o espaço extra usado por mergesort talvez não seja um problema e se o programador está usando tipos primitivos talvez o desempenho seja a coisa mais importante, então use o quicksort .

Por exemplo: Este é o exemplo ao classificar questões de estabilidade.

insira a descrição da imagem aqui

É por isso que classificações estáveis ​​fazem sentido para tipos de objetos, especialmente tipos de objetos mutáveis ​​e tipos de objetos com mais dados do que apenas a chave de classificação, e mergesort é esse tipo de classificação. Mas, para os tipos primitivos, a estabilidade não é apenas irrelevante. Não tem sentido.

Fonte: INFO

Dinesh Kumar
fonte
0

O Arrays.sortmétodo Java usa quicksort, inserção por inserção e mergesort. Há até mesmo um quicksort de pivô único e duplo implementado no código OpenJDK. O algoritmo de classificação mais rápido depende das circunstâncias e os vencedores são: classificação por inserção para pequenos arrays (47 atualmente escolhidos), mergesort para arrays principalmente classificados e quicksort para os arrays restantes, então Array.sort () do Java tenta escolher o melhor algoritmo para aplicar com base nesses critérios.

David McManamon
fonte