Мне нужно отсортировать Map<Key, Value> по значениям.
Поскольку значения не уникальны, я обнаруживаю, что преобразую keySet в array и сортирую этот массив с помощью сортировки по массиву с помощью пользовательского компаратора, который сортирует значение, связанное с ключом.
Есть ли более простой способ?
Переведено автоматически
Ответ 1
Вот универсальная версия:
publicclassMapUtil { publicstatic <K, V extendsComparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) { List<Entry<K, V>> list = newArrayList<>(map.entrySet()); list.sort(Entry.comparingByValue());
Map<K, V> result = newLinkedHashMap<>(); for (Entry<K, V> entry : list) { result.put(entry.getKey(), entry.getValue()); }
return result; } }
Ответ 2
Java 8 предлагает новый ответ: преобразуйте записи в поток и используйте комбинаторы компаратора из Map.Entry:
Это позволит вам использовать записи, отсортированные в порядке возрастания значения. Если вам нужно значение по убыванию, просто переверните компаратор:
Затем вы можете перейти к использованию других потоковых операций для использования данных. Например, если вы хотите, чтобы на новой карте были 10 лучших:
Этот код может быть поврежден несколькими способами. Если вы собираетесь использовать предоставленный код, обязательно прочитайте комментарии, чтобы быть в курсе последствий. Например, значения больше не могут быть получены по их ключу. (get всегда возвращается null.)
Это кажется намного проще, чем все предыдущее. Используйте древовидную карту следующим образом:
// Note: this comparator imposes orderings that are inconsistent with // equals. publicintcompare(String a, String b) { if (base.get(a) >= base.get(b)) { return -1; } else { return1; } // returning 0 would merge keys } }
Которая создаст функцию (объект) для карты [которая принимает любой из ключей в качестве входных данных, возвращая соответствующее значение], а затем применит к ним естественный (сопоставимый) порядок [значений].
Если они несопоставимы, то вам нужно будет сделать что-то вроде
ПРИМЕЧАНИЕ: Если вы собираетесь использовать древовидную карту, помните, что если сравнение == 0, то элемент уже есть в списке (что произойдет, если у вас есть несколько значений, которые сравнивают одно и то же). Чтобы облегчить это, вы могли бы добавить свой ключ в компаратор следующим образом (предполагая, что ваши ключи и значения Comparable):
= Примените естественный порядок к значению, отображенному ключом, и соедините его с естественным порядком ключа
Обратите внимание, что это все равно не сработает, если ваши ключи сравниваются с 0, но этого должно быть достаточно для большинства comparable элементов (поскольку hashCode, equals и compareTo часто синхронизированы ...)
необходимо выполнить, учитывая полную готовую карту
Не пытайтесь использовать приведенные выше компараторы для a TreeMap; нет смысла пытаться сравнивать вставленный ключ, если у него нет значения, до окончания ввода, т. Е. Он очень быстро сломается
Point 1 is a bit of a deal-breaker for me; google collections is incredibly lazy (which is good: you can do pretty much every operation in an instant; the real work is done when you start using the result), and this requires copying a whole map!
"Full" answer/Live sorted map by values
Don't worry though; if you were obsessed enough with having a "live" map sorted in this manner, you could solve not one but both(!) of the above issues with something crazy like the following:
Note: This has changed significantly in June 2012 - the previous code could never work: an internal HashMap is required to lookup the values without creating an infinite loop between the TreeMap.get() -> compare() and compare() -> get()
classValueComparableMap<K extendsComparable<K>,V> extendsTreeMap<K,V> { //A map for doing lookups on the keys for comparison so we don't get infinite loops privatefinal Map<K, V> valueMap;
ValueComparableMap(final Ordering<? super V> partialValueOrdering) { this(partialValueOrdering, newHashMap<K,V>()); }
privateValueComparableMap(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 returnsuper.put(k, v); //Put it in value order }
publicstaticvoidmain(String[] args){ TreeMap<String, Integer> map = newValueComparableMap<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")); } }
When we put, we ensure that the hash map has the value for the comparator, and then put to the TreeSet for sorting. But before that we check the hash map to see that the key is not actually a duplicate. Also, the comparator that we create will also include the key so that duplicate values don't delete the non-duplicate keys (due to == comparison). These 2 items are vital for ensuring the map contract is kept; if you think you don't want that, then you're almost at the point of reversing the map entirely (to Map<V,K>).