Je veux écrire un comparateur qui me permettra de trier un TreeMap par valeur au lieu de l'ordre naturel par défaut.
J'ai essayé quelque chose comme ça, mais je ne trouve pas ce qui ne va pas:
import java.util.*;
class treeMap {
public static void main(String[] args) {
System.out.println("the main");
byValue cmp = new byValue();
Map<String, Integer> map = new TreeMap<String, Integer>(cmp);
map.put("de",10);
map.put("ab", 20);
map.put("a",5);
for (Map.Entry<String,Integer> pair: map.entrySet()) {
System.out.println(pair.getKey()+":"+pair.getValue());
}
}
}
class byValue implements Comparator<Map.Entry<String,Integer>> {
public int compare(Map.Entry<String,Integer> e1, Map.Entry<String,Integer> e2) {
if (e1.getValue() < e2.getValue()){
return 1;
} else if (e1.getValue() == e2.getValue()) {
return 0;
} else {
return -1;
}
}
}
Je suppose que ce que je demande est: puis-je obtenir un Map.Entry
passage au comparateur?
Réponses:
Vous ne pouvez pas avoir le
TreeMap
tri lui - même sur les valeurs, car cela défie laSortedMap
spécification:Cependant, en utilisant une collection externe, vous pouvez toujours trier
Map.entrySet()
comme vous le souhaitez, soit par clés, valeurs, ou même une combinaison (!!) des deux.Voici une méthode générique qui retourne a
SortedSet
ofMap.Entry
, étant donné aMap
dont les valeurs sontComparable
:Vous pouvez maintenant effectuer les opérations suivantes:
Notez que des trucs géniaux se produiront si vous essayez de modifier soit le
SortedSet
lui - même, soit l'Map.Entry
intérieur, car ce n'est plus une "vue" de la carte originale comme l'entrySet()
est.D'une manière générale, la nécessité de trier les entrées d'une carte par ses valeurs est atypique.
Remarque sur
==
pourInteger
Votre comparateur d'origine compare en
Integer
utilisant==
. C'est presque toujours faux, car==
avec lesInteger
opérandes est une égalité de référence, pas une égalité de valeur.Questions connexes
new Integer(i) == i
dans Java? (OUI!!!)la source
La réponse des lubrifiants polygéniques est presque parfaite. Il a cependant un bug important. Il ne gérera pas les entrées de mappage dont les valeurs sont identiques.
Ce code: ...
Produirait:
Remarquez comment notre vache a disparu en partageant la valeur «1» avec notre singe: O!
Cette modification du code résout ce problème:
la source
Set
implémentation ne contient un élément plus d'une fois. Vous venez de violer cette contrainte. De l'SortedSet
API: Notez que l'ordre maintenu par un ensemble triée doit être compatible avec égaux ... . La solution serait de passer à uneList
implémentation.Set
s ici. Puisque leComparator
viole leSet
contratSet.remove
,Set.contains
etc. ne fonctionne pas ! Vérifiez cet exemple sur ideone .int res= e1.getValue().compareTo(e2.getValue());
àint res= e2.getValue().compareTo(e1.getValue());
, vous avez un ordre des valeurs décroissant au lieu de croissant.res != 0 ? res : e1.getKey().compareTo(e2.getKey())
pour conserver l'ordre des clés avec des valeurs égales.Dans Java 8:
la source
A
TreeMap
est toujours trié par les clés, tout le reste est impossible. AComparator
vous permet simplement de contrôler comment les clés sont triées.Si vous voulez les valeurs triées, vous devez les extraire dans un
List
et les trier.la source
Cela ne peut pas être fait en utilisant a
Comparator
, car il obtiendra toujours la clé de la carte à comparer.TreeMap
ne peut trier que par clé.la source
SortedMap
laquelle spécifie le tri par clés) beginnersbook.com/2014/07/...Comparator
utilise une carte existante pour obtenir les valeurs à trier. En d'autres termes, il ne peut pas trier les valeurs arbitraires placées dans leTreeMap
dernier, uniquement les valeurs qui sont déjà dans la carte d'origine.La réponse d'Olof est bonne, mais il lui faut encore une chose avant d'être parfaite. Dans les commentaires ci-dessous sa réponse, dacwe souligne (correctement) que son implémentation viole le contrat Compare / Equals pour les Sets. Si vous essayez d'appeler contient ou de supprimer une entrée qui est clairement dans l'ensemble, l'ensemble ne la reconnaîtra pas en raison du code qui permet aux entrées de valeurs égales d'être placées dans l'ensemble. Donc, pour résoudre ce problème, nous devons tester l'égalité entre les clés:
"Notez que l'ordre maintenu par un ensemble trié (qu'un comparateur explicite soit fourni ou non) doit être cohérent avec égal si l'ensemble trié doit implémenter correctement l'interface Set ... l'interface Set est définie en termes de l'opération d'égalité , mais un ensemble trié effectue toutes les comparaisons d'éléments en utilisant sa méthode compareTo (ou compare), de sorte que deux éléments jugés égaux par cette méthode sont, du point de vue de l'ensemble trié, égaux . " ( http://docs.oracle.com/javase/6/docs/api/java/util/SortedSet.html )
Comme nous avions à l'origine négligé l'égalité afin de forcer l'ensemble à ajouter des entrées de valeur égale, nous devons maintenant tester l'égalité dans les clés pour que l'ensemble renvoie réellement l'entrée que vous recherchez. C'est un peu compliqué et ce n'est certainement pas la façon dont les ensembles étaient censés être utilisés - mais cela fonctionne.
la source
Je sais que cet article demande spécifiquement de trier un TreeMap par valeurs, mais pour ceux d'entre nous qui ne se soucient pas vraiment de la mise en œuvre mais qui veulent une solution qui garde la collection triée au fur et à mesure que les éléments sont ajoutés, j'apprécierais les commentaires sur ce TreeSet Solution. D'une part, les éléments ne sont pas facilement récupérés par clé, mais pour le cas d'utilisation que j'avais sous la main (trouver les n clés avec les valeurs les plus basses), ce n'était pas une exigence.
la source
Beaucoup de gens entendent qu'il est conseillé d'utiliser List et je préfère l'utiliser également
voici deux méthodes dont vous avez besoin pour trier les entrées de la carte en fonction de leurs valeurs.
la source