Si j'ai un Map
comme ceci:
HashMap<Integer, ComparableObject> map;
et je souhaite obtenir une collection de valeurs triées par ordre naturel, quelle méthode est la plus rapide?
Créez une instance d'une collection triable telle que ArrayList
, ajoutez les valeurs, puis triez-la:
List<ComparableObject> sortedCollection = new ArrayList<ComparableObject>(map.values());
Collections.sort(sortedCollection);
Créez une instance d'une collection ordonnée comme TreeSet
, puis ajoutez les valeurs:
Set<ComparableObject> sortedCollection = new TreeSet<ComparableObject>(map.values());
Notez que la collection résultante n'est jamais modifiée, le tri ne doit donc avoir lieu qu'une seule fois.
TreeSet a une garantie de complexité temporelle log(n)
pour les méthodes add()/remove()/contains()
. Le tri d'un ArrayList
prend des opérations n*log(n)
, mais add()/get()
ne prend que 1
.
Donc, si vous récupérez principalement et que vous ne triez pas souvent, ArrayList
est le meilleur choix. Si vous triez souvent, mais que vous n'en récupérez pas autant, TreeSet
serait un meilleur choix.
Théoriquement, le tri à la fin devrait être plus rapide . Le maintien de l'état de trié tout au long du processus pourrait nécessiter un temps processeur supplémentaire.
Du point de vue du CS, les deux opérations sont NlogN, mais une sorte devrait avoir une constante inférieure.
Pourquoi ne pas utiliser le meilleur des deux mondes? Si vous ne l'utilisez plus, triez à l'aide d'un TreeSet et initialisez un ArrayList avec le contenu.
List<ComparableObject> sortedCollection =
new ArrayList<ComparableObject>(
new TreeSet<ComparableObject>(map.values()));
MODIFIER:
J'ai créé un point de référence (vous pouvez y accéder à l'adresse suivante: Pastebin.com/5pyPMJav ) pour tester les trois approches (ArrayList + Collections.sort, TreeSet et la meilleure approche que je puisse adopter) et la mienne gagne toujours. Le fichier de test crée une carte avec 10000 éléments, dont les valeurs ont un comparateur intentionnellement terrible, puis chacune des trois stratégies a une chance de a) trier les données et b) de les parcourir. Voici un exemple de sortie (vous pouvez le tester vous-même):
EDIT: J'ai ajouté un aspect qui enregistre les appels à Thingy.compareTo (Thingy) et j'ai ajouté une nouvelle stratégie basée sur PriorityQueues qui est beaucoup plus rapide que les solutions précédentes (du moins pour le tri).
compareTo() calls:123490
Transformer ArrayListTransformer
Creation: 255885873 ns (0.255885873 seconds)
Iteration: 2582591 ns (0.002582591 seconds)
Item count: 10000
compareTo() calls:121665
Transformer TreeSetTransformer
Creation: 199893004 ns (0.199893004 seconds)
Iteration: 4848242 ns (0.004848242 seconds)
Item count: 10000
compareTo() calls:121665
Transformer BestOfBothWorldsTransformer
Creation: 216952504 ns (0.216952504 seconds)
Iteration: 1604604 ns (0.001604604 seconds)
Item count: 10000
compareTo() calls:18819
Transformer PriorityQueueTransformer
Creation: 35119198 ns (0.035119198 seconds)
Iteration: 2803639 ns (0.002803639 seconds)
Item count: 10000
Étrangement, mon approche fonctionne mieux en itération (j'aurais pensé qu'il n'y aurait aucune différence avec l'approche ArrayList en itération, ai-je un bug dans mon repère?)
Déni de responsabilité: Je sais que ceci est probablement une très mauvaise référence, mais cela aide à vous faire comprendre le message et je ne l’ai certainement pas manipulé pour que mon approche gagne.
(Le code a une dépendance à Apache commons/lang pour les constructeurs equals/hashcode/compareTo, mais il devrait être facile de le refactoriser)
Assurez-vous de lire mon commentaire sur TreeSet en bas si vous choisissez d'implémenter B)
Si votre application effectue uniquement des tris occasionnels mais effectue plusieurs itérations, je dirais que vous feriez mieux d'utiliser une liste simple, non triée. Triez-le une fois et profitez ensuite d'une itération plus rapide. L'itération est particulièrement rapide sur une liste de tableaux.
Cependant, si vous souhaitez que l'ordre de tri soit garanti à tout moment ou que vous ajoutiez/supprimiez souvent des éléments, utilisez une collection triée et prenez le hit en itération.
Donc, dans votre cas, je dirais que A) est la meilleure option. La liste est triée une fois, ne change pas et tire donc avantage d'être un tableau. L'itération devrait être très rapide, surtout si vous savez que c'est un ArrayList et que vous pouvez directement utiliser ArrayList.get () au lieu d'un Iterator.
J'ajouterais également que TreeSet est par définition un ensemble, ce qui signifie que les objets sont uniques. Un TreeSet détermine l'égalité en utilisant compareTo sur votre comparateur/comparable. Vous pourriez facilement trouver vous-même des données manquantes si vous essayez d'ajouter deux objets dont compareTo renvoie la valeur 0. p. Ex. ajouter "C", "A", "B", "A" à un TreeSet retournera "A", "B", "C"
Collections.sort
utilise mergeSort qui a O (nlog n).
TreeSet
a un arbre rouge-noir sous-jacent, les opérations de base ont O (logn). Donc n éléments a aussi O (nlog n).
Donc, les deux sont le même grand algorithme O.