¿Por qué Collections.sort usa Mergesort pero Arrays.sort no?

96

Estoy usando JDK-8 (x64). Para Arrays.sort(primitivas) encontré lo siguiente en la documentación de Java:

El algoritmo de clasificación es un Quicksort de doble pivote de Vladimir Yaroslavskiy, Jon Bentley y Joshua Bloch.

Para Collections.sort(objetos) encontré este "Timsort":

Esta implementación es un mergesort iterativo, adaptable, estable ... Esta implementación vuelca la lista especificada en una matriz, ordena la matriz e itera sobre la lista restableciendo cada elemento desde la posición correspondiente en la matriz.

Si Collections.sortusa una matriz, ¿por qué no simplemente llama Arrays.sorto usa QuickSort de doble pivote ? ¿Por qué utilizar Mergesort ?

Quest Monger
fuente
8
Ese es el javadoc para matrices de primitivas: las matrices de Objetos se ordenan usando meregsort.
Assylias
2
mergesort da u siempre nlogn mientras que la clasificación rápida puede dar algún nlogn2 geneally matrices de tamaño no es tan grande, pero colecciones fácilmente va hasta millones de entradas por lo que tomar un riesgo de nlogn2 no vale la pena PS nlogn2 i significa sqaure de n
Kumar Saurabh
O (n ^ 2) para clasificación rápida es el peor de los casos extremos. En la práctica es más rápido
James Wierzba
pero no puedes ignorar esos caese mientras haces una API
Kumar Saurabh
2
Este enlace está muy relacionado.
qartal

Respuestas:

99

La API garantiza una clasificación estable que Quicksort no ofrece. Sin embargo, al ordenar los valores primitivos por su orden natural, no notará una diferencia ya que los valores primitivos no tienen identidad. Por lo tanto, Quicksort se puede usar para matrices primitivas y se usará cuando se considere más eficiente¹.

En el caso de los objetos, puede observar cuando los objetos con una identidad diferente que se consideran iguales según su equalsimplementación o la proporcionada Comparatorcambian su orden. Por lo tanto, Quicksort no es una opción. Entonces se usa una variante de MergeSort , las versiones actuales de Java usan TimSort . Esto se aplica a ambos Arrays.sorty Collections.sort, aunque con Java 8, el Listmismo puede anular los algoritmos de ordenación.


¹ La ventaja de eficiencia de Quicksort es que necesita menos memoria cuando se realiza en el lugar. Pero tiene un rendimiento dramático en el peor de los casos y no puede explotar las ejecuciones de datos preordenados en una matriz, lo que hace TimSort .

Por lo tanto, los algoritmos de clasificación se modificaron de una versión a otra, mientras permanecían en la clase que ahora tiene un nombre engañoso DualPivotQuicksort. Además, la documentación no se puso al día, lo que muestra que, en general, es una mala idea nombrar un algoritmo utilizado internamente en una especificación, cuando no es necesario.

La situación actual (incluyendo Java 8 a Java 11) es la siguiente:

  • Generalmente, los métodos de clasificación para matrices primitivas usarán Quicksort solo bajo ciertas circunstancias. Para arreglos más grandes, intentarán identificar primero las ejecuciones de datos preordenados , como lo hace TimSort , y los fusionará cuando la cantidad de ejecuciones no supere un cierto umbral. De lo contrario, recurrirán al ordenamiento rápido , pero con una implementación que recurrirá al ordenamiento por inserción para rangos pequeños, lo que no solo afecta a las matrices pequeñas, sino también a la recursividad del ordenamiento rápido.
  • sort(char[],…)y sort(short[],…)agregue otro caso especial, para usar la clasificación de conteo para matrices cuya longitud exceda un cierto umbral
  • Del mismo modo, sort(byte[],…)usará la ordenación por conteo , pero con un umbral mucho más pequeño, lo que crea el mayor contraste con la documentación, ya que sort(byte[],…)nunca usa ordenación rápida. Solo usa la ordenación por inserción para matrices pequeñas y la ordenación por conteo en caso contrario.
Holger
fuente
1
Hmm, curiosamente el Javadoc de Collections.sort dice: "Se garantiza que este tipo sea estable", pero dado que delega a List.sort, que puede ser anulado por implementaciones de lista, la clasificación estable no puede ser garantizada por Collections.sort para todas las listas. implementaciones. ¿O me pierdo algo? Y List.sort no requiere que el alogirthm de clasificación sea estable.
Puce
11
@Puce: eso simplemente significa que la responsabilidad de esa garantía ahora está en manos de quienes implementan el List.sortmétodo primordial . Collections.sortnunca podría garantizar el funcionamiento correcto para cada Listimplementación, ya que no puede garantizar, por ejemplo, que Listno cambie su contenido de manera falsa. Todo se reduce a que la garantía de Collections.sortsolo se aplica a Listimplementaciones correctas (y correctas Comparatoro equalsimplementaciones).
Holger
1
@Puce: Pero tiene razón, el Javadoc no es igualmente explícito sobre esta restricción en ambos métodos. Pero al menos la documentación más reciente indica que Collections.sortdelegará en List.sort.
Holger
@Puce: hay toneladas de ejemplos de esto, donde las propiedades importantes no son parte del tipo, sino que solo se mencionan en la documentación (y, por lo tanto, el compilador no las verifica). El sistema de tipos de Java es simplemente demasiado débil para expresar propiedades interesantes. (No es muy diferente de un lenguaje tipado dinámicamente en este sentido, allí también, las propiedades están definidas en la documentación y depende del programador asegurarse de que no se violen). En realidad, va más allá: ¿se dio cuenta? que Collections.sortni siquiera menciona en su firma de tipo que la salida está ordenada?
Jörg W Mittag
1
En un lenguaje con un sistema de tipos más expresivo, el tipo de retorno de Collections.sortsería algo así como "una colección del mismo tipo y longitud que la entrada con las propiedades de que 1) cada elemento presente en la entrada también está presente en la salida, 2 ) para cada par de elementos de la salida, el de la izquierda no es mayor que el de la derecha, 3) para cada par de elementos iguales de la salida, el índice de la izquierda en la entrada es más pequeño que el de la derecha "o algo así como ese.
Jörg W Mittag
20

No sé acerca de la documentación, pero la implementación de java.util.Collections#sortJava 8 (HotSpot) es así:

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

Y List#sorttiene esta implementación:

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

Entonces, al final, los Collections#sortusos Arrays#sort(de elementos de objeto) detrás de escena. Esta implementación utiliza ordenación por fusión o ordenación por tiempo.

Luiggi Mendoza
fuente
16

Según el Javadoc, solo las matrices primitivas se ordenan usando Quicksort. Las matrices de objetos también se ordenan con Mergesort.

Por lo tanto, Collections.sort parece usar el mismo algoritmo de clasificación que Arrays.sort para Objetos.

Otra pregunta sería por qué se usa un algoritmo de clasificación diferente para matrices primitivas que para matrices de objetos.

Pardo rojizo
fuente
2

Como se indica en muchas de las respuestas.

Arrays.sort utiliza Quicksort para ordenar colecciones primitivas porque no se requiere estabilidad (no sabrá ni le importará si se intercambiaron dos entradas idénticas en la ordenación)

Arrays.sort usa MergeSort o más específicamente Timsort para ordenar colecciones de objetos. Se requiere estabilidad. Quicksort no proporciona estabilidad, Timsort sí.

Collections.sort delega a Arrays.sort, por lo que ve el javadoc haciendo referencia a MergeSort.

cogitoboy
fuente
1

La ordenación rápida tiene dos inconvenientes importantes cuando se trata de la ordenación combinada:

  • No es estable mientras que no es primitivo.
  • No garantiza el rendimiento de n log n.

La estabilidad no es un problema para los tipos primitivos, ya que no existe una noción de identidad a diferencia de la igualdad (de valores).

La estabilidad es un gran problema al clasificar objetos arbitrarios. Es un buen beneficio adicional que Merge Sort garantiza un rendimiento n log n (tiempo) sin importar la entrada. Es por eso que se selecciona la clasificación por combinación para proporcionar una clasificación estable (clasificación por combinación) para ordenar las referencias de objetos.

Krutik
fuente
1
¿Qué quiere decir con "No estable"?
Arun Gowda