Trois réponses d'une ligne...
J'utiliserais Collections Google Goyave pour le faire - si vos valeurs sont Comparable
alors vous pouvez utiliser
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
Ce qui créera une fonction (objet) pour la carte [qui prend n'importe laquelle des clés en entrée et renvoie la valeur correspondante], puis leur appliquera un ordre naturel (comparable) [les valeurs].
S'ils ne sont pas comparables, vous devrez faire quelque chose du type suivant
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Celles-ci peuvent être appliquées à un TreeMap (en tant que Ordering
étend Comparator
), ou un LinkedHashMap après un certain tri
NB : Si vous comptez utiliser un TreeMap, n'oubliez pas que si une comparaison == 0, alors l'élément se trouve déjà dans la liste (ce qui se produira si vous avez plusieurs valeurs dont la comparaison est identique). Pour pallier ce problème, vous pouvez ajouter votre clé au comparateur comme suit (en supposant que vos clés et valeurs sont Comparable
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
\= Appliquer l'ordre naturel à la valeur représentée par la clé, et le combiner avec l'ordre naturel de la clé.
Notez que cela ne fonctionnera toujours pas si vos clés sont comparées à 0, mais cela devrait être suffisant pour la plupart des cas. comparable
articles (comme hashCode
, equals
y compareTo
sont souvent synchronisées...)
Véase Commande.onResultOf() y Fonctions.forMap() .
Mise en œuvre
Maintenant que nous avons un comparateur qui fait ce que nous voulons, nous devons en obtenir un résultat.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Maintenant, cela va très probablement fonctionner, mais :
- doit être fait pour obtenir une carte complète et finie
- N'essayez pas les comparateurs ci-dessus sur une
TreeMap
il est inutile d'essayer de comparer une clé insérée si elle n'a pas de valeur avant le put, c'est-à-dire qu'il y aura une rupture très rapide.
Le point 1 est un peu rédhibitoire pour moi ; google collections est incroyablement paresseux (ce qui est bien : vous pouvez faire pratiquement 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 un fichier todo carte !
Réponse "complète" / carte vivante triée par valeurs
Mais ne vous inquiétez pas : si vous êtes suffisamment obsédé par le fait d'avoir une carte "vivante" triée de cette manière, vous pouvez résoudre non pas l'un mais les deux ( !) des problèmes ci-dessus avec quelque chose de fou comme ce qui suit :
Note : Ceci a changé de manière significative en juin 2012 - le code précédent ne pourrait jamais fonctionner : un HashMap interne est nécessaire pour rechercher les valeurs sans créer une boucle infinie entre le TreeMap.get()
-> compare()
y compare()
-> get()
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import com.google.common.base.Functions;
import com.google.common.collect.Ordering;
class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
//A map for doing lookups on the keys for comparison so we don't get infinite loops
private final Map<K, V> valueMap;
ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
this(partialValueOrdering, new HashMap<K,V>());
}
private ValueComparableMap(Ordering<? super V> partialValueOrdering,
HashMap<K, V> valueMap) {
super(partialValueOrdering //Apply the value ordering
.onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
.compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
this.valueMap = valueMap;
}
public V put(K k, V v) {
if (valueMap.containsKey(k)){
//remove the key in the sorted set before adding the key again
remove(k);
}
valueMap.put(k,v); //To get "real" unsorted values for the comparator
return super.put(k, v); //Put it in value order
}
public static void main(String[] args){
TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
map.put("a", 5);
map.put("b", 1);
map.put("c", 3);
assertEquals("b",map.firstKey());
assertEquals("a",map.lastKey());
map.put("d",0);
assertEquals("d",map.firstKey());
//ensure it's still a map (by overwriting a key, but with a new value)
map.put("d", 2);
assertEquals("b", map.firstKey());
//Ensure multiple values do not clobber keys
map.put("e", 2);
assertEquals(5, map.size());
assertEquals(2, (int) map.get("e"));
assertEquals(2, (int) map.get("d"));
}
}
Lorsque nous mettons, nous nous assurons que la carte de hachage a la valeur pour le comparateur, puis nous mettons dans le TreeSet pour le tri. Mais avant cela, nous vérifions la carte de hachage pour voir si la clé n'est pas en fait un doublon. De plus, le comparateur que nous créons inclura également la clé afin que les valeurs dupliquées ne suppriment pas les clés non dupliquées (en raison de la comparaison ==). Ces 2 éléments sont vital pour s'assurer que le contrat de la carte est conservé ; si vous pensez que vous ne voulez pas cela, alors vous êtes presque au point d'inverser la carte entièrement (à Map<V,K>
).
Le constructeur devrait être appelé comme
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
28 votes
Une carte n'est pas faite pour être triée, mais pour être consultée rapidement. Les valeurs égales des objets rompent la contrainte de la carte. Utilisez l'ensemble des entrées, comme
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
yCollections.sort ....
c'est comme ça.2 votes
Un cas où cela peut se produire est celui où nous essayons d'utiliser un compteur en Java (Map<Object, Integer>). Le tri par nombre d'occurrences serait alors une opération courante. Un langage comme Python possède une structure de données Counter intégrée. Pour une autre façon de l'implémenter en Java, aquí est un exemple
11 votes
Il y a beaucoup de cas d'utilisation pour les cartes triées, c'est pourquoi vous avez TreeMap et ConcurrentSkipListMap dans le jdk.
0 votes
Voir aussi stackoverflow.com/questions/7860822/
7 votes
TreeMap et ConcurrentSkipListMap trient par clé. La question porte sur le tri par valeur.
1 votes
J'aimerais ajouter que en fonction de votre cas d'utilisation il peut être raisonnable de simplement conserver un double du TreeMap qui associe votre valeur à vos clés. Par exemple, votre carte normale peut avoir "a" -> 5, "b" -> 7". Et votre carte "triée" peut avoir 5 -> "a", 7 -> "b". Il vous suffit d'utiliser la carte appropriée à différents endroits et de faire un effort pour toujours modifier les deux cartes ensemble. Ce n'est pas très joli et il y a beaucoup de réserves et d'hypothèses, mais pour algunos Dans certains cas, il peut s'agir d'une réponse simple et efficace par rapport à toutes les autres réponses qui reposent sur un tri actif des valeurs.