Drei einzeilige Antworten ...
Ich würde Google Collections Guava verwenden , um dies zu tun - wenn Ihre Werte sind Comparable
, können Sie verwenden
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
Dadurch wird eine Funktion (ein Objekt) für die Karte erstellt [die eine der Tasten als Eingabe verwendet und den jeweiligen Wert zurückgibt] und dann eine natürliche (vergleichbare) Reihenfolge auf sie [die Werte] angewendet.
Wenn sie nicht vergleichbar sind, müssen Sie etwas in der Art von tun
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Diese können nach einer Sortierung auf eine TreeMap (wie Ordering
erweitert Comparator
) oder eine LinkedHashMap angewendet werden
NB : Wenn Sie eine TreeMap verwenden möchten, denken Sie daran, dass bei einem Vergleich == 0 das Element bereits in der Liste enthalten ist (was passiert, wenn Sie mehrere Werte haben, die dasselbe vergleichen). Um dies zu vermeiden, können Sie Ihren Schlüssel wie folgt zum Komparator hinzufügen (vorausgesetzt, Ihre Schlüssel und Werte sind Comparable
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= Wenden Sie eine natürliche Reihenfolge auf den vom Schlüssel zugeordneten Wert an und kombinieren Sie diese mit der natürlichen Reihenfolge des Schlüssels
Beachten Sie, dass dies immer noch nicht funktionieren , wenn Sie Ihre Schlüssel auf 0 zu vergleichen, aber dies sollte für die meisten ausreichend sein , comparable
Gegenstände (wie hashCode
, equals
und compareTo
sind oft synchron ...)
Siehe Ordering.onResultOf () und Functions.forMap () .
Implementierung
Jetzt, da wir einen Komparator haben, der macht, was wir wollen, müssen wir ein Ergebnis daraus ziehen.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Nun wird dies höchstwahrscheinlich funktionieren, aber:
- muss mit einer vollständigen fertigen Karte durchgeführt werden
- Versuchen Sie nicht die Komparatoren oben auf einem
TreeMap
; Es macht keinen Sinn, einen eingefügten Schlüssel zu vergleichen, wenn er erst nach dem Put einen Wert hat, dh er bricht sehr schnell
Punkt 1 ist für mich ein Deal-Breaker. Google-Sammlungen sind unglaublich faul (was gut ist: Sie können so ziemlich jeden Vorgang sofort ausführen; die eigentliche Arbeit ist erledigt, wenn Sie das Ergebnis verwenden), und dies erfordert das Kopieren einer ganzen Karte!
"Vollständige" Antwort / Live sortierte Karte nach Werten
Mach dir aber keine Sorgen; Wenn Sie genug davon besessen wären, eine "Live" -Karte auf diese Weise sortieren zu lassen, könnten Sie nicht 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 niemals funktionieren: Zum Nachschlagen der Werte ist eine interne HashMap erforderlich, ohne eine Endlosschleife zwischen TreeMap.get()
-> compare()
und compare()
-> zu erstellenget()
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 setzen, stellen wir sicher, dass die Hash-Map den Wert für den Komparator hat, und legen sie dann zum Sortieren in das TreeSet. Zuvor überprüfen wir jedoch die Hash-Map, um festzustellen, dass der Schlüssel kein Duplikat ist. Der von uns erstellte Komparator enthält auch den Schlüssel, damit doppelte Werte die nicht doppelten Schlüssel nicht löschen (aufgrund von == Vergleich). Diese beiden Elemente sind wichtig, um sicherzustellen, dass der Kartenvertrag eingehalten wird. Wenn Sie glauben, dass Sie das nicht wollen, sind Sie fast am Punkt, die Karte vollständig umzukehren Map<V,K>
.
Der Konstruktor müsste als aufgerufen werden
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
undCollections.sort ....
so.