Drei 1-zeilige Antworten...
Ich würde verwenden Google-Sammlungen Guave um dies zu tun - wenn Ihre Werte sind Comparable
dann können Sie
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
Dadurch wird eine Funktion (ein Objekt) für die Map erstellt [die jeden der Schlüssel als Eingabe annimmt und den entsprechenden Wert zurückgibt], und dann eine natürliche (vergleichbare) Ordnung auf sie [die Werte] angewendet.
Wenn sie nicht vergleichbar sind, müssen Sie etwas tun, das in etwa wie folgt aussieht
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Diese können auf eine TreeMap angewendet werden (als Ordering
erweitert Comparator
), oder eine LinkedHashMap nach einiger Sortierung
NB : Wenn Sie eine TreeMap verwenden wollen, denken Sie daran, dass, wenn ein Vergleich == 0 ist, das Element bereits in der Liste ist (was passiert, wenn Sie mehrere Werte haben, die gleich sind). Um dies zu vermeiden, können Sie Ihren Schlüssel dem Vergleicher wie folgt hinzufügen (vorausgesetzt, Ihre Schlüssel und Werte sind Comparable
) :
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
\= Natürliche Ordnung auf den durch den Schlüssel abgebildeten Wert anwenden und diese mit der natürlichen Ordnung des Schlüssels verbinden
Beachten Sie, dass dies immer noch nicht funktioniert, wenn Ihre Schlüssel mit 0 verglichen werden, aber dies sollte für die meisten Fälle ausreichend sein comparable
Artikel (wie hashCode
, equals
y compareTo
sind oft synchron...)
Siehe Bestellung.onResultOf() y Functions.forMap() .
Umsetzung
Da wir nun einen Komparator haben, der das tut, was wir wollen, müssen wir ein Ergebnis von ihm erhalten.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Das wird höchstwahrscheinlich auch funktionieren, aber:
- muss angesichts einer vollständigen fertigen Karte durchgeführt werden
- Versuchen Sie nicht, die obigen Komparatoren an einem
TreeMap
Es macht keinen Sinn, einen eingefügten Schlüssel zu vergleichen, wenn er erst nach dem Put einen Wert hat, d. h., er bricht sehr schnell ab.
Punkt 1 ist ein bisschen ein Deal-Breaker für mich; Google Sammlungen ist unglaublich faul (was gut ist: Sie können so ziemlich jede Operation in einem Augenblick zu tun; die eigentliche Arbeit ist getan, wenn Sie beginnen, das Ergebnis zu verwenden), und dies erfordert das Kopieren einer ganz Karte!
"Vollständige" Antwort/Live sortierte Karte nach Werten
Aber keine Sorge, wenn Sie so besessen davon wären, eine "Live"-Karte auf diese Weise zu sortieren, könnten Sie nicht nur eines, sondern beide(!) der oben genannten Probleme mit etwas Verrücktem wie dem Folgenden lösen:
Hinweis: Dies hat sich im Juni 2012 erheblich geändert - der vorherige Code konnte nie funktionieren: eine interne HashMap ist erforderlich, um die Werte nachzuschlagen, ohne eine Endlosschleife zwischen den 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"));
}
}
Wenn wir put, stellen wir sicher, dass die Hash-Map den Wert für den Komparator hat, und dann put zum TreeSet für die Sortierung. Aber vorher überprüfen wir die Hash-Map, um zu sehen, dass der Schlüssel kein Duplikat ist. Außerdem wird der Komparator, den wir erstellen, auch den Schlüssel enthalten, damit doppelte Werte nicht die nicht doppelten Schlüssel löschen (aufgrund des == Vergleichs). Diese 2 Elemente sind vital um sicherzustellen, dass der Kartenvertrag eingehalten wird; wenn Sie meinen, dass Sie das nicht wollen, dann sind Sie fast an dem Punkt, an dem Sie die Karte komplett umkehren müssen (zu Map<V,K>
).
Der Konstruktor müsste aufgerufen werden als
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));