Три однострочных ответа ...
Я бы использовал Коллекции Google Гуава для этого - если ваши значения , вы можете использовать
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
Которая создаст функцию (объект) для карты [которая принимает любой из ключей в качестве входных данных, возвращая соответствующее значение], а затем применяет естественный (сопоставимый) порядок к ним [значениям].
Если они не сопоставимы, вам нужно сделать что-то вроде
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Их можно применить к TreeMap (поскольку Ordering
расширяет Comparator
) или к LinkedHashMap после некоторой сортировки
NB : если вы собираетесь использовать TreeMap, помните, что если сравнение == 0, то элемент уже есть в списке (что произойдет, если у вас есть несколько значений, которые сравнивают одинаковые ). Чтобы облегчить это, вы можете добавить свой ключ в компаратор следующим образом (при условии, что ваши ключи и значения равны Comparable
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= Примените естественный порядок к значению, отображаемому ключом, и соедините его с естественным порядком ключа
Обратите внимание, что это все равно не будет работать, если ваши ключи сравниваются с 0, но этого должно быть достаточно для большинства элементов comparable
(поскольку hashCode
, equals
и compareTo
часто синхронизируются ...)
См. Ordering.onResultOf () и Functions.forMap ().
Реализация
Итак, теперь, когда у нас есть компаратор, который делает то, что мы хотим, нам нужно получить от него результат.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Теперь это, скорее всего, будет работать, но:
- необходимо выполнить с учетом полностью готовой карты
- Не пытайтесь использовать приведенные выше компараторы на
TreeMap
; нет смысла пытаться сравнить вставленный ключ, если он не имеет значения до тех пор, пока не будет помещен, т.е. он сломается очень быстро
Пункт 1 для меня немного мешает; Коллекции google невероятно ленивы (что хорошо: вы можете выполнять практически все операции в одно мгновение; настоящая работа выполняется, когда вы начинаете использовать результат), и для этого требуется скопировать целую карту!
"Полный" ответ / живая карта с сортировкой по значениям
Но не волнуйтесь; если бы вы были достаточно одержимы такой сортировкой «живой» карты, вы могли бы решить не одну, а обе (!) из вышеперечисленных проблем с помощью чего-то безумного, например:
Примечание: это существенно изменилось в июне 2012 года - предыдущий код никогда не мог работать: требуется внутренняя HashMap для поиска значений без создания бесконечного цикла между TreeMap.get()
-> compare()
и 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"));
}
}
Когда мы помещаем, мы гарантируем, что хэш-карта имеет значение для компаратора, а затем помещаем в TreeSet для сортировки. Но перед этим мы проверяем хеш-карту, чтобы убедиться, что ключ на самом деле не дубликат. Кроме того, созданный нами компаратор также будет включать ключ, чтобы повторяющиеся значения не удаляли неповторяющиеся ключи (из-за == сравнения).
Эти 2 элемента являются жизненно важными для обеспечения сохранения контракта на карту; если вы думаете, что этого не хотите, то вы почти готовы полностью перевернуть карту (на Map<V,K>
).
Конструктор должен вызываться как
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
Карта не предназначена для сортировки, а для быстрого доступа к ней. Равные значения объектов нарушают ограничение карты. Используйте набор записей, например
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
иCollections.sort ....
таким образом.Случай, когда это может возникнуть, когда мы пытаемся использовать счетчик в Java (Map <Object, Integer>). Тогда сортировка по количеству вхождений будет обычной операцией. Такой язык, как Python, имеет встроенную структуру данных Counter. Для альтернативного способа реализации на Java, здесь является примером
Существует множество вариантов использования отсортированных карт, поэтому у вас есть TreeMap и ConcurrentSkipListMap в jdk.
См. Также coderhelper.com/questions/7860822/…
TreeMap и ConcurrentSkipListMap сортируются по ключу. Вопрос в сортировке по значению.
Я хотел бы добавить, что , в зависимости от вашего варианта использования , может быть разумным просто сохранить дубликат TreeMap, который сопоставляет ваше значение с вашими ключами. Например, ваша обычная карта может иметь «a» -> 5, «b» -> 7 ». А ваша« отсортированная »карта может иметь 5 ->« a », 7 ->« b ». Вы бы просто использовали то, что map подходит в разных местах и старается всегда изменять две карты вместе. Это некрасиво, и есть много предостережений и предположений, но для некоторых случаев это может быть простым и эффективным ответом по сравнению со всеми главные ответы здесь, которые полагаются на активную сортировку ваших ценностей.