web-dev-qa-db-fra.com

Java 7 utilise-t-il Tim Sort pour la méthode Arrays.Sort?

Je ne trouve pas la documentation de Java 7, je ne peux trouver que sur le Java 6, qui est encore rapide ou fusionné. Est-ce que quelqu'un sait comment trouver la documentation de la méthode Arrays.sort in Java 7?

49
Osvaldo

Java 7 utilise le tri rapide à double pivot pour les primitives et TimSort pour les objets.

Selon le doc API Java 7 pour les primitives:

Remarque sur la mise en œuvre: l'algorithme de tri est un tri rapide à double pivot de Vladimir Yaroslavskiy, Jon Bentley et Joshua Bloch. Cet algorithme offre des performances O (n log (n)) sur de nombreux ensembles de données qui provoquent la dégradation d'autres performances en performances quadratiques et est généralement plus rapide que les implémentations Quicksort traditionnelles (un pivot).

Selon le doc API Java 7 pour les objets:

L'implémentation a été adaptée de la liste de tri de Tim Peters pour Python (TimSort). Elle utilise les techniques de Peter McIlroy "Optimistic Sorting and Information Theoretic Complexity", dans les actes du quatrième symposium annuel ACM-SIAM sur Discrete Algorithms, pp 467-474, janvier 1993.

Timsort est un "tri par fusion et tri par insertion" hybride.

Je ne sais pas si cela est très différent de ce qu'il était en Java 6, for Arrays.sort JDK6:

un tri rapide réglé, adapté de "Engineering a Sort Function" de Jon L. Bentley et M. Douglas McIlroy, Software-Practice and Experience, Vol. 23 (11) P. 1249-1265 (novembre 1993)

Pour Object [] ou les collections (Collections.sort ()), le tri par fusion est utilisé.

79
Michael Borgwardt

Oui! ... et aussi non.

Résumé

Dans l'Open JDK actuel  implémentations Tim Sort est généralement utilisé pour trier les tableaux d'objets (c'est-à-dire Arrays.sort(Object[]) et amis) - mais pour tableaux primitifs (le reste de la Arrays.sort Méthodes) une variété d'autres méthodes sont utilisées.

Pour les primitives, l'heuristique choisit parmi une variété de méthodes de tri comme le tri rapide, le tri par fusion, le tri par comptage3. en fonction des données triées. La plupart de ces décisions sont simplement prises à l'avance en fonction du type et de la taille du tableau en cours de tri, mais pour les éléments int et long, la décision est en fait adaptative en fonction du tri mesuré du tableau. Vous avez donc l'adaptation/introspection (l'heuristique pour choisir un algorithme) en plus de l'adaptation/introspection (TimSort ou tri de fusion similaire) dans de nombreux cas!

Détails

Tim Sort est utilisé pour la plupart des sortes d'objets, tels que Arrays.sort(Object[] a), sauf si l'utilisateur a spécifiquement demandé le comportement hérité en définissant la propriété système Java.util.Arrays.useLegacyMergeSort Sur true.

Pour les primitifs, la situation est plus complexe. Au moins à partir de JDK 8 (version 1.8.0_111), Une variété d'heurstiques sont utilisées en fonction de la taille des tableaux triés, du type primitif et du "tri" mesuré du tableau. Voici un aperçu:

  • Pour tous les types primitifs autres que les octets1, les tableaux de moins de 47 éléments sont simplement triés en utilisant le tri par insertion (voir DualPivotQuicksort.INSERTION_SORT_THRESHOLD). Ce seuil est aussi utilisé lors du tri des sous-tableaux qui surviennent lorsque la fusion ou le tri rapide sont utilisés et que la taille du sous-tableau tombe en dessous du seuil. Ainsi, une sorte de tri par insertion sera utilisée dans toutes sortes, et pour les petits tableaux, c'est le seul algorithme utilisé.
  • Pour les types primitifs byte, short et char, un tri de comptage est utilisé pour les tableaux de grande taille. Il s'agit d'un tri simple qui prend O(n + range) temps, où range est le nombre total d'octets (256) ou de valeurs courtes/char (65536). Le tri implique l'allocation d'un tableau sous-jacent de valeurs range, il n'est donc utilisé que lorsque le nombre d'éléments à trier est une fraction significative de la plage totale. En particulier, il est utilisé pour les tableaux d'octets supérieurs à 29 éléments (soit ~ 11% de la plage) et les tableaux courts/caractères supérieurs à 3200 éléments (~ 5% de la plage).
  • Pour les tableaux d'octets, l'une des deux approches ci-dessus est toujours utilisée.
  • Pour les tableaux int et long au-dessus du seuil de tri d'insertion et pour les tableaux short/char à la fois au-dessus du seuil de tri d'insertion et en dessous du seuil de tri de comptage, un de deux algorithmes peuvent être utilisés: tri rapide à double pivot ou tri par fusion. Celui qui est utilisé dépend d'une mesure du tri du tableau: l'entrée est divisée en s'exécute d'éléments ascendants ou descendants. Si le nombre de ces exécutions est supérieur à 66, le tableau est considéré comme non trié et est trié avec tri rapide à double pivot. Sinon, le tableau est considéré comme principalement trié et le mergesort est utilisé (en utilisant les exécutions déjà énumérées comme point de départ).

Le idée de trouver des runs puis d'utiliser mergesort pour les trier est en fait très similaire à TimSort, bien qu'il y ait quelques différences. Donc, au moins pour certains paramètres, le JDK utilise un mergesort sensible à l'exécution, mais pour de nombreuses autres combinaisons de paramètres, il utilise un algorithme différent, et au moins 5 algorithmes distincts sont utilisés au total!

Raisonnement

Le raisonnement derrière le comportement de tri différent de Object[] Par rapport à primitif est probablement au moins double:

1) Les tris de Object[] Doivent être stables: les objets qui trient également apparaîtront dans le même ordre que l'entrée. Pour les tableaux primitifs, un tel concept n'existe pas: les primitives sont entièrement définies par leur valeur, il n'y a donc pas de distinction entre un type stable et un type instable. Cela permet aux tris primitifs de s'affranchir du besoin d'algorithmes stables en faveur de la vitesse.

2) Les tris de Object[] Doivent impliquer la méthode Object.compare(), qui peut être arbitrairement complexe et coûteuse. Même si la méthode compare() est simple, il y aura généralement un surdébit d'appel de méthode à moins que la méthode de tri entière ne puisse être intégrée2. Ainsi, des sortes de Object[] Seront généralement biaisées pour minimiser les comparaisons totales, même au prix d'une complexité algorithmique supplémentaire.

Les tris de tableaux primitifs, d'autre part, comparent juste directement les valeurs primitives qui prennent généralement de l'ordre d'un cycle ou deux. Dans ce cas, l'algorithme doit être optimisé en tenant compte à la fois du coût des comparaisons et de l'algorithme environnant, car ils sont probablement de même ampleur.


Au moins pour les versions entre Java 7 et Java 9, et bien sûr cela inclut également le JDK d'Oracle car il est basé sur Open JDK. Il s'agit de vraisemblablement que d'autres implémentations utilisent une approche similaire, mais je n'ai pas vérifié.

1 Pour les tableaux d'octets, le seuil de tri par insertion est effectivement de 29 éléments, car c'est le seuil inférieur au-dessus duquel le tri de comptage est utilisé.

2 Cela semble peu probable, car il est assez important.

3 Le tri par comptage n'est utilisé que pour les valeurs avec une plage relativement limitée de 16 bits ou moins: byte, short ou char.

21
BeeOnRope

Oui, Java 7 utilisera Timsort pour Arrays.sort. Voici le commit: http://hg.openjdk.Java.net/jdk7/jdk7/jdk/rev/ bfd7abda8f79

20
Jonny Heggheim