Trois réponses sur une ligne ...
J'utiliserais Google Collections Guava pour ce 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, 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
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Ceux-ci peuvent être appliqués à un TreeMap (sous forme d' Ordering
extension Comparator
) ou à un LinkedHashMap après un certain tri
NB : 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
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= 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 (comme hashCode
, equals
et compareTo
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.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Maintenant, cela fonctionnera très probablement, mais:
- doit être fait étant donné une carte complète terminée
- N'essayez pas les comparateurs ci-dessus sur un
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 rapidement
Le 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()
et 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 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
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
et deCollections.sort ....
cette façon.