Je suis relativement nouveau à Java et trouve souvent que je dois trier un Map<Key, Value>
sur les valeurs.
Étant donné que les valeurs ne sont pas uniques, je me retrouve à convertir le keySet
en un array
et à trier ce tableau via le tri de tableau avec un comparateur personnalisé qui trie la valeur associée à la clé.
Existe-t-il un moyen plus simple?
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
et deCollections.sort ....
cette façon.Réponses:
Voici une version générique:
la source
forEachOrdered
place deforEach
, puisque la documentation desforEach
états: "Le comportement de cette opération est explicitement non déterministe."?Note importante:
Ce code peut se casser de plusieurs manières. Si vous avez l'intention d'utiliser le code fourni, assurez-vous de lire également les commentaires pour être conscient des implications. Par exemple, les valeurs ne peuvent plus être récupérées par leur clé. (
get
revient toujoursnull
.)Cela semble beaucoup plus facile que tout ce qui précède. Utilisez un TreeMap comme suit:
Production:
la source
return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))
?map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
Java 8 offre une nouvelle réponse: convertir les entrées en un flux et utiliser les combinateurs de comparaison de Map.Entry:
Cela vous permettra de consommer les entrées triées par ordre croissant de valeur. Si vous voulez une valeur décroissante, inversez simplement le comparateur:
Si les valeurs ne sont pas comparables, vous pouvez passer un comparateur explicite:
Vous pouvez ensuite utiliser d'autres opérations de flux pour consommer les données. Par exemple, si vous voulez le top 10 dans une nouvelle carte:
Ou imprimez à
System.out
:la source
parallelStream()
dans ce cas?Trois réponses sur une ligne ...
J'utiliserais
Google CollectionsGuava pour ce faire - si vos valeurs sontComparable
alors vous pouvez utiliserCe qui créera une fonction (objet) pour la carte [qui prend n'importe laquelle des clés en entrée, renvoyant la valeur respective], puis leur applique un ordre naturel (comparable) [les valeurs].
S'ils ne sont pas comparables, vous devrez faire quelque chose dans le sens de
Ceux-ci peuvent être appliqués à un TreeMap (sous forme d'
Ordering
extensionComparator
) ou à un LinkedHashMap après un certain triNB : Si vous allez utiliser un TreeMap, n'oubliez pas que si une comparaison == 0, alors l'élément est déjà dans la liste (ce qui se produira si vous avez plusieurs valeurs qui se comparent les mêmes). Pour remédier à cela, vous pouvez ajouter votre clé au comparateur comme cela (en supposant que vos clés et vos valeurs sont
Comparable
):= Appliquer un ordre naturel à la valeur mappée par la clé et la combiner avec l'ordre naturel de la clé
Notez que cela ne fonctionnera toujours pas si vos clés se comparent à 0, mais cela devrait être suffisant pour la plupart des
comparable
éléments (commehashCode
,equals
etcompareTo
sont souvent synchronisés ...)Voir Ordering.onResultOf () et Functions.forMap () .
la mise en oeuvre
Alors maintenant que nous avons un comparateur qui fait ce que nous voulons, nous devons en obtenir un résultat.
Maintenant, cela fonctionnera très probablement, mais:
TreeMap
; il ne sert à rien d'essayer de comparer une clé insérée quand elle n'a pas de valeur avant le put, c'est-à-dire qu'elle se cassera très rapidementLe point 1 est un peu une rupture pour moi; google collections est incroyablement paresseux (ce qui est bien: vous pouvez faire à peu près toutes les opérations en un instant; le vrai travail est fait lorsque vous commencez à utiliser le résultat), et cela nécessite de copier toute une carte!
Réponse "complète" / Carte triée en direct par valeurs
Ne vous inquiétez pas cependant; si vous étiez assez obsédé par le fait d'avoir une carte "en direct" triée de cette manière, vous pourriez résoudre non pas un mais les deux (!) des problèmes ci-dessus avec quelque chose de fou comme le suivant:
Remarque: Cela a considérablement changé en juin 2012 - le code précédent ne pouvait jamais fonctionner: un HashMap interne est requis pour rechercher les valeurs sans créer une boucle infinie entre les
TreeMap.get()
->compare()
etcompare()
->get()
Lorsque nous mettons, nous nous assurons que la carte de hachage a la valeur pour le comparateur, puis placée dans le TreeSet pour le tri. Mais avant cela, nous vérifions la carte de hachage pour voir que la clé n'est pas réellement un doublon. De plus, le comparateur que nous créons inclura également la clé afin que les valeurs en double ne suppriment pas les clés non en double (en raison de la comparaison ==). Ces 2 éléments sont essentiels pour garantir le respect du contrat de carte; si vous pensez que vous ne voulez pas cela, alors vous êtes presque sur le point d'inverser complètement la carte (vers
Map<V,K>
).Le constructeur devrait être appelé comme
la source
Ordering
est tout simplement un richeComparator
. J'ai essayé de commenter chaque exemple (l'italique en dessous de chacun). "naturel" indique que les objets sontComparable
; c'est comme ComparableComparator d'apache common.onResultOf
applique une fonction à l'élément comparé. Donc, si vous aviez une fonction qui ajoutait 1 à un entier, celanatural().onResultOf(add1Function).compare(1,2)
finirait par le faire2.compareTo(3)
ImmutableSetMultiMap
ouImmutableListMultiMap
contenir la collection de variables en double.Depuis http://www.programmersheaven.com/download/49349/download.aspx
la source
Avec Java 8, vous pouvez utiliser l' api des flux pour le faire de manière beaucoup moins verbeuse:
la source
Collections.reverseOrder(comparing(Entry::getValue))
Entry.comparingByValue(Comparator.reverseOrder())
Le tri des clés nécessite que le comparateur recherche chaque valeur pour chaque comparaison. Une solution plus évolutive utiliserait directement le set d'entrée, car alors la valeur serait immédiatement disponible pour chaque comparaison (bien que je n'ai pas sauvegardé cela par des chiffres).
Voici une version générique d'une telle chose:
Il existe des moyens de réduire la rotation de la mémoire pour la solution ci-dessus. La première ArrayList créée pourrait par exemple être réutilisée comme valeur de retour; cela nécessiterait la suppression de certains avertissements génériques, mais cela pourrait valoir la peine pour le code de bibliothèque réutilisable. De plus, le comparateur n'a pas besoin d'être réalloué à chaque appel.
Voici une version plus efficace mais moins attrayante:
Enfin, si vous devez accéder en continu aux informations triées (plutôt que de les trier de temps en temps), vous pouvez utiliser une carte multiple supplémentaire. Faites-moi savoir si vous avez besoin de plus de détails ...
la source
La bibliothèque commons-collections contient une solution appelée TreeBidiMap . Vous pouvez également consulter l'API Google Collections. Il a TreeMultimap que vous pouvez utiliser.
Et si vous ne voulez pas utiliser ces frameworks ... ils sont livrés avec du code source.
la source
J'ai regardé les réponses données, mais beaucoup d'entre elles sont plus compliquées que nécessaire ou suppriment des éléments de carte lorsque plusieurs clés ont la même valeur.
Voici une solution qui, je pense, convient mieux:
Notez que la carte est triée de la valeur la plus élevée à la plus faible.
la source
Pour ce faire avec les nouvelles fonctionnalités de Java 8:
Les entrées sont classées par leurs valeurs à l'aide du comparateur donné. Alternativement, si vos valeurs sont mutuellement comparables, aucun comparateur explicite n'est nécessaire:
La liste renvoyée est un instantané de la carte donnée au moment où cette méthode est appelée, donc aucune ne reflétera les modifications ultérieures de l'autre. Pour une vue itérable en direct de la carte:
L'itérable retourné crée un nouvel instantané de la carte donnée à chaque itération, donc à moins de modifications simultanées, il reflétera toujours l'état actuel de la carte.
la source
Créez un comparateur personnalisé et utilisez-le lors de la création d'un nouvel objet TreeMap.
Utilisez le code ci-dessous dans votre fonction principale
Production:
la source
Bien que je convienne que le besoin constant de trier une carte est probablement une odeur, je pense que le code suivant est le moyen le plus simple de le faire sans utiliser une structure de données différente.
}
Et voici un test unitaire embarrassant et incomplet:
}
Le résultat est une liste triée d'objets Map.Entry, à partir de laquelle vous pouvez obtenir les clés et les valeurs.
la source
Utilisez un comparateur générique tel que:
la source
La réponse votée pour le plus ne fonctionne pas lorsque vous avez 2 éléments égaux. TreeMap laisse des valeurs égales.
l'exmaple: carte non triée
résultats
Alors laisse de côté E !!
Pour moi, cela a bien fonctionné pour ajuster le comparateur, s'il est égal, ne retournez pas 0 mais -1.
dans l'exemple:
maintenant il revient:
carte non triée:
résultats:
en réponse à Aliens (2011 nov. 22): J'utilise cette solution pour une carte des identifiants et des noms entiers, mais l'idée est la même, donc le code ci-dessus n'est peut-être pas correct (je l'écrirai dans un test et vous donner le bon code), voici le code pour un tri de carte, basé sur la solution ci-dessus:
et ceci est la classe de test (je viens de la tester, et cela fonctionne pour l'Integer, String Map:
voici le code du comparateur d'une carte:
et voici le test pour cela:
bien sûr, vous pouvez rendre cela beaucoup plus générique, mais j'en avais juste besoin pour 1 cas (la carte)
la source
Au lieu d'utiliser
Collections.sort
comme certains, je suggère d'utiliserArrays.sort
. En fait, ceCollections.sort
qui se passe est quelque chose comme ceci:Il appelle simplement
toArray
sur la liste et utilise ensuiteArrays.sort
. De cette façon, toutes les entrées de carte seront copiées trois fois: une fois de la carte dans la liste temporaire (que ce soit une LinkedList ou ArrayList), puis dans le tableau temporaire et enfin dans la nouvelle carte.Ma solution supprime cette étape car elle ne crée pas de LinkedList inutile. Voici le code, générique et optimisé pour les performances:
la source
Il s'agit d'une variante de la réponse d'Anthony, qui ne fonctionne pas s'il existe des valeurs en double:
Notez que c'est plutôt en l'air comment gérer les valeurs nulles.
Un avantage important de cette approche est qu'elle renvoie en fait une carte, contrairement à certaines des autres solutions proposées ici.
la source
Meilleure approche
Production
la source
Un problème majeur. Si vous utilisez la première réponse (Google vous emmène ici), changez le comparateur pour ajouter une clause égale, sinon vous ne pouvez pas obtenir de valeurs de sorted_map par clés:
la source
Il y a déjà beaucoup de réponses à cette question, mais aucune ne m'a fourni ce que je cherchais, une implémentation de carte qui renvoie les clés et les entrées triées par la valeur associée, et conserve cette propriété lorsque les clés et les valeurs sont modifiées dans la carte. Deux autres questions le demandent spécifiquement.
J'ai concocté un exemple générique convivial qui résout ce cas d'utilisation. Cette implémentation n'honore pas tous les contrats de l'interface Map, tels que la prise en compte des changements de valeur et des suppressions dans les ensembles renvoyés par keySet () et entrySet () dans l'objet d'origine. Je pensais qu'une telle solution serait trop importante pour être incluse dans une réponse Stack Overflow. Si je parviens à créer une implémentation plus complète, je vais peut-être la publier sur Github, puis sur ce lien dans une version mise à jour de cette réponse.
la source
Entrée tardive.
Avec l'avènement de Java-8, nous pouvons utiliser des flux pour la manipulation de données de manière très simple / succincte. Vous pouvez utiliser des flux pour trier les entrées de mappage par valeur et créer un LinkedHashMap qui préserve l' itération de l' ordre d'insertion .
Par exemple:
Pour l'ordre inverse, remplacez:
avec
la source
Entry.comparingByValue()
(comme réponse assylias ci-dessus stackoverflow.com/a/22132422/1480587 ) oucomparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
que vous avez utilisé? Je comprends que vous comparez également les clés si les valeurs sont identiques, non? J'ai remarqué que le tri conserve l'ordre des éléments avec la même valeur - le tri par clés est-il donc nécessaire si les clés sont déjà triées auparavant?Étant donné la carte
Trier la carte en fonction de la valeur dans l'ordre croissant
Trier la carte en fonction de la valeur dans l'ordre décroissant
Production:
{logiciel = 50, technologie = 70, États-Unis = 100, emplois = 200, opportunité = 200}
{jobs = 200, opportunité = 200, USA = 100, technologie = 70, logiciel = 50}
la source
Selon le contexte, en utilisant
java.util.LinkedHashMap<T>
ce qui se souvient de l'ordre dans lequel les éléments sont placés dans la carte. Sinon, si vous avez besoin de trier les valeurs en fonction de leur ordre naturel, je recommanderais de maintenir une liste séparée qui peut être triée viaCollections.sort()
.la source
Puisque TreeMap <> ne fonctionne pas pour des valeurs qui peuvent être égales, j'ai utilisé ceci:
Vous voudrez peut-être mettre une liste dans un LinkedHashMap , mais si vous ne voulez que l'itérer tout de suite, c'est superflu ...
la source
C'est trop compliqué. Les cartes n'étaient pas censées faire un travail tel que les trier par valeur. Le moyen le plus simple consiste à créer votre propre classe afin qu'elle corresponde à vos besoins.
Dans l'exemple ci-dessous, vous êtes censé ajouter TreeMap un comparateur à l'endroit où * est. Mais par l'API Java, il ne donne que des clés de comparateur, pas des valeurs. Tous les exemples indiqués ici sont basés sur 2 cartes. Un hachage et un nouvel arbre. Ce qui est étrange.
L'exemple:
Alors changez la carte en un ensemble de cette façon:
Vous allez créer de la classe
Results
,et la classe Comparateur:
De cette façon, vous pouvez facilement ajouter plus de dépendances.
Et comme dernier point, j'ajouterai un itérateur simple:
la source
Basé sur le code @devinmoore, une méthode de tri de carte utilisant des génériques et prenant en charge l'ordre croissant et décroissant.
la source
Voici une solution OO (c'est-à-dire qu'elle n'utilise pas de
static
méthodes):Par la présente fait don au domaine public.
la source
Afaik la manière la plus propre est d'utiliser des collections pour trier la carte selon la valeur:
la source
Quelques changements simples afin d'avoir une carte triée avec des paires qui ont des valeurs en double. Dans la méthode de comparaison (classe ValueComparator) lorsque les valeurs sont égales ne renvoient pas 0 mais renvoient le résultat de la comparaison des 2 clés. Les clés sont distinctes dans une carte afin que vous réussissiez à conserver les valeurs en double (qui sont en fait triées par clés). Ainsi, l'exemple ci-dessus pourrait être modifié comme ceci:
la source
Bien sûr, la solution de Stephen est vraiment géniale, mais pour ceux qui ne peuvent pas utiliser Guava:
Voici ma solution pour trier par valeur une carte. Cette solution gère le cas où il y a deux fois la même valeur, etc.
L'exéc: http://www.ideone.com/dq3Lu
Le résultat:
J'espère que cela aidera certaines personnes
la source
Si vous avez des clés en double et seulement un petit ensemble de données (<1000) et que votre code n'est pas critique en termes de performances, vous pouvez simplement faire ce qui suit:
inputUnsortedMap est l'entrée du code.
La variable sortedOutputMap contiendra les données en ordre décroissant lors de l'itération. Pour changer l'ordre, remplacez simplement> par un <dans l'instruction if.
N'est pas le tri le plus rapide mais fait le travail sans dépendances supplémentaires.
la source