Trzy odpowiedzi 1-liniowe ...
W tym celu skorzystałbym z Google Guava Kolekcje - jeśli twoje wartości są Comparable
, możesz użyć
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
Który utworzy funkcję (obiekt) dla mapy [która pobiera dowolny z klawiszy jako dane wejściowe, zwracając odpowiednią wartość], a następnie zastosuje do nich naturalne (porównywalne) uporządkowanie [wartości].
Jeśli nie są porównywalne, musisz zrobić coś w stylu
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Można je zastosować do TreeMap (w Ordering
rozszerzeniu Comparator
) lub LinkedHashMap po pewnym sortowaniu
Uwaga : Jeśli zamierzasz użyć TreeMap, pamiętaj, że jeśli porównanie == 0, to element jest już na liście (co się stanie, jeśli masz wiele wartości, które porównują to samo). Aby to złagodzić, możesz dodać swój klucz do komparatora w taki sposób (zakładając, że twoje klucze i wartości są Comparable
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= Zastosuj naturalne uporządkowanie do wartości odwzorowanej przez klucz i połącz to z naturalnym uporządkowaniem klucza
Zauważ, że to nadal nie zadziała, jeśli twoje klucze będą miały wartość 0, ale powinno wystarczyć dla większości comparable
przedmiotów (as hashCode
, equals
icompareTo
często są zsynchronizowane ...)
Zobacz Ordering.onResultOf () i Functions.forMap () .
Realizacja
Teraz, gdy mamy komparator, który robi to, co chcemy, musimy uzyskać z tego wynik.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Teraz najprawdopodobniej zadziała, ale:
- należy to zrobić, biorąc pod uwagę kompletną gotową mapę
- Nie próbuj powyższych komparatorów na
TreeMap
; nie ma sensu porównywać wstawionego klucza, gdy nie ma on wartości aż do momentu wstawienia, tzn. bardzo szybko się psuje
Punkt 1 jest dla mnie trochę przełomowy; kolekcje google są niezwykle leniwe (co jest dobre: możesz wykonać niemal każdą operację w jednej chwili; prawdziwa praca jest wykonywana, gdy zaczniesz używać wyniku), a to wymaga skopiowania całej mapy!
Odpowiedź „pełna” / mapa posortowana na żywo według wartości
Nie martw się jednak; jeśli miałeś dość obsesji na punkcie posortowanej w ten sposób mapy „na żywo”, możesz rozwiązać nie jeden, ale oba (!) z powyższych problemów, używając czegoś szalonego, takiego jak:
Uwaga: uległo to znacznej zmianie w czerwcu 2012 r. - poprzedni kod nigdy nie mógł działać: wewnętrzny HashMap jest wymagany do wyszukiwania wartości bez tworzenia nieskończonej pętli między TreeMap.get()
-> compare()
a 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"));
}
}
Kiedy wstawiamy, upewniamy się, że mapa skrótu ma wartość dla komparatora, a następnie umieszczana w TreeSet w celu sortowania. Ale wcześniej sprawdzamy mapę skrótu, aby zobaczyć, czy klucz nie jest w rzeczywistości duplikatem. Tworzony przez nas komparator będzie również zawierał klucz, aby zduplikowane wartości nie usuwały niepowielonych kluczy (z powodu porównania ==). Te 2 elementy są niezbędne do zachowania kontraktu na mapie; jeśli uważasz, że tego nie chcesz, to prawie jesteś w stanie całkowicie odwrócić mapę (doMap<V,K>
).
Konstruktor musiałby zostać nazwany jako
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
iCollections.sort ....
tak.