49 Вопрос: Сортировать карту <Key, Value> по значениям

вопрос создан в Sat, Feb 24, 2018 12:00 AM

Я относительно новичок в Java и часто нахожу, что мне нужно отсортировать Map<Key, Value> по значениям.

Поскольку значения не являются уникальными, я обнаружил, что преобразовываю keySet в array и сортирую этот массив через сортировку по массиву с помощью пользовательского компаратора , который сортирует по значению связан с ключом.

Есть ли более простой способ?

    
1519
  1. Карта не предназначена для сортировки, а для быстрого доступа. Объект равных значений нарушает ограничение карты. Используйте набор ввода, например List<Map.Entry<...>> list =new LinkedList(map.entrySet()) и Collections.sort ...., таким образом.
    2014-02-09 17: 34: 42Z
  2. Случай, когда это может возникнуть, когда мы пытаемся использовать счетчик в Java (Map < Object, Integer >). Сортировка по количеству вхождений будет обычной операцией. Такой язык, как Python, имеет встроенную структуру данных Counter. Для альтернативного способа реализации в Java здесь является пример
    2017-12-21 20: 03: 58Z
  3. Существует много вариантов использования для отсортированных карт, поэтому у вас есть TreeMap и ConcurrentSkipListMap в jdk.
    2018-04-22 19: 10: 45Z
30 ответов                              30                         

Вот общая версия:

public class MapUtil {
    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        List<Entry<K, V>> list = new ArrayList<>(map.entrySet());
        list.sort(Entry.comparingByValue());

        Map<K, V> result = new LinkedHashMap<>();
        for (Entry<K, V> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }

        return result;
    }
}
    
844
2018-04-03 00: 00: 27Z
  1. Рад, что это помогает. Джон, LinkedHashMap важен для решения, так как он обеспечивает предсказуемый порядок итераций.
    2012-07-01 12: 46: 16Z
  2. @ buzz3791 True. Это будет иметь место в любом алгоритме сортировки. Изменение значения узлов в структуре во время сортировки приводит к непредсказуемым (и почти всегда плохим) результатам.
    2013-04-25 18: 55: 17Z
  3. @ Sheagorath Я пробовал в Android, и он тоже работает. Это не проблема конкретной платформы, учитывая, что вы используете версию Java 6. Вы правильно реализовали Comparable в своем объекте значения?
    2014-12-08 01: 12: 29Z
  4. Разве в версии Java 8 не следует использовать forEachOrdered вместо forEach, поскольку в документах forEach говорится: «Поведение этой операции явно недетерминировано."?
    2015-06-27 14: 06: 30Z
  5. полностью разорвал это, но зачислил @CarterPage в комментариях (это будет в любом случае в проекте с открытым исходным кодом). Большое спасибо.
    2015-09-02 03: 07: 48Z

Важное примечание:

Этот код может сломаться несколькими способами. Если вы намереваетесь использовать предоставленный код, обязательно прочитайте также комментарии, чтобы знать о последствиях. Например, значения больше не могут быть получены по их ключу. (get всегда возвращает null.)

Это кажется намного проще, чем все вышесказанное. Используйте TreeMap следующим образом:

public class Testing {
    public static void main(String[] args) {
        HashMap<String, Double> map = new HashMap<String, Double>();
        ValueComparator bvc = new ValueComparator(map);
        TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);

        map.put("A", 99.5);
        map.put("B", 67.4);
        map.put("C", 67.4);
        map.put("D", 67.3);

        System.out.println("unsorted map: " + map);
        sorted_map.putAll(map);
        System.out.println("results: " + sorted_map);
    }
}

class ValueComparator implements Comparator<String> {
    Map<String, Double> base;

    public ValueComparator(Map<String, Double> base) {
        this.base = base;
    }

    // Note: this comparator imposes orderings that are inconsistent with
    // equals.
    public int compare(String a, String b) {
        if (base.get(a) >= base.get(b)) {
            return -1;
        } else {
            return 1;
        } // returning 0 would merge keys
    }
}

Вывод:

unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4}
results: {D=67.3, B=67.4, C=67.4, A=99.5}
    
407
2016-04-18 23: 41: 20Z
  1. Больше нет ( stackoverflow.com/questions/109383/… ). Кроме того, почему был приведен дубль? Разве это не должно быть return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))?
    2010-08-11 21: 50: 54Z
  2. @ Стивен: Нет. В этом случае все равные по значению ключи отбрасываются (разница между равными и сравнением по ссылке). Дополнительно: даже у этого кода есть проблемы со следующей последовательностью map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
    2010-08-20 07: 00: 23Z
  3. Компаратор, используемый для древовидной карты, несовместим с equals (см. javadox sortMap). Это означает, что удаление элементов с древовидной карты не будет работать. sorted_map.get ("A") вернет ноль. Это означает, что использование древовидной карты не работает.
    2010-12-01 14: 36: 50Z
  4. На всякий случай, если людям непонятно: это решение, вероятно, не будет делать то, что вы хотите, если у вас несколько ключей, сопоставляемых одному значению - только один из этих ключей появится в отсортированном результате.
    2011-11-24 04: 37: 55Z
  5. Луи Вассерман (да, один из ребят из Google Guava), на самом деле очень не любит этот ответ: «Он ломается несколькими очень запутанными способами, если вы даже посмотрите на него Забавно. Если карта поддержки меняется, она сломается. Если несколько ключей отображаются на одно и то же значение, она сломается. Если вы вызовете get для ключа, которого нет на карте поддержки, он сломается. Если вы что-нибудь сделаете вообще это приведет к поиску ключа, которого нет на карте, - вызова Map.equals, содержит ключ, что угодно - он порвет с очень странными следами стека ». plus.google.com/102216152814616302326/posts/bEQLDK712MJ
    2012-07-03 21: 19: 30Z

Java 8 предлагает новый ответ: преобразовать записи в поток и использовать комбинаторы компараторов из Map.Entry:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue());

Это позволит вам использовать записи, отсортированные в порядке возрастания значений. Если вы хотите уменьшить значение, просто поменяйте местами компаратор:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));

Если значения несопоставимы, вы можете передать явный компаратор:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(comparator));

Затем вы можете перейти к использованию других потоковых операций для получения данных. Например, если вы хотите топ-10 на новой карте:

Map<K,V> topTen =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
       .limit(10)
       .collect(Collectors.toMap(
          Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

Или распечатайте в System.out:

map.entrySet().stream()
   .sorted(Map.Entry.comparingByValue())
   .forEach(System.out::println);
    
287
2017-11-25 22: 16: 36Z
  1. Хорошо, но как насчет использования parallelStream() в этом случае?
    2014-12-09 18: 21: 54Z
  2. Он будет работать параллельно, однако вы можете обнаружить, что стоимость объединения карт для объединения частичных результатов слишком высока, и параллельная версия может работать не так хорошо, как ты бы надеялся. Но это работает и дает правильный ответ.
    2014-12-09 18: 37: 25Z
  3. Спасибо за ваш полезный совет. Это было именно то, что мне было интересно, хотя это зависит от того, какой тип ключа вы используете и сколько параметров ... Важно то, что «он работает и дает правильный ответ».
    2014-12-10 19: 30: 49Z
  4. вам не нужно использовать compareByValue в примере top10?
    2016-08-19 23: 34: 01Z
  5. @ Benj будет работать с точки зрения извлечения топ-10, но полученная карта больше не будет упорядочена.
    2019-06-18 14: 30: 36Z

Три однострочных ответа ...

Я бы использовал Коллекции Google Гуава для этого - если ваши значения Comparable, вы можете использовать

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);

Теперь это, скорее всего, сработает, но:

  1. должно быть сделано с полной готовой картой
  2. Не пытайтесь использовать приведенные выше компараторы для TreeMap; нет смысла пытаться сравнивать вставленный ключ, когда он не имеет значения, до тех пор, пока после ввода не произойдет, т.е. он сломается очень быстро

Точка 1 для меня немного нарушает условия сделки; Коллекции Google невероятно ленивы (и это хорошо: вы можете выполнять практически каждую операцию за один раз; настоящая работа выполняется, когда вы начинаете использовать результат), и для этого необходимо скопировать целую карту!

«Полный» ответ /Live отсортированная карта по значениям

Не беспокойся; если вы были достаточно одержимы сортировкой «живой» карты таким образом, вы могли бы решить не одну, а обе (!) из вышеперечисленных проблем с помощью чего-то сумасшедшего, например:

Примечание. Это значительно изменилось в июне 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));
    
209
2017-05-23 12: 26: 34Z
  1. Привет, @Stephen, можете ли вы привести пример, как использовать Ordering? Я смотрю на исходный код Ordering и совершенно не могу понять, что .natural (). OnResultOf (...) возвращает! Исходный код "public < F > Ordering < F > onResultOf", я даже не знаю, как он компилируется! Самое главное, как использовать "< F > Ordering < F >" отсортировать карту? Это компаратор или что-то? Спасибо.
    2010-11-10 10: 58: 52Z
  2. Ordering - это просто обогащенный Comparator. Я пробовал комментировать каждый пример (курсив под каждым). «натуральный» означает, что объектами являются Comparable; это как ComparableComparator Apache Common. onResultOf применяет функцию к сравниваемому элементу. Таким образом, если бы у вас была функция, которая добавила 1 к целому числу, то natural().onResultOf(add1Function).compare(1,2) в конечном итоге делали бы 2.compareTo(3)
    2010-11-11 11: 44: 54Z
  3. ImmutableSortedMap.copyOf создает исключение IllegalArgumentException, если в исходной карте есть повторяющиеся значения.
    2013-04-30 09: 39: 17Z
  4. @ Ibalazscs Да, так и будет - вы должны иметь возможность использовать ImmutableSetMultiMap или ImmutableListMultiMap для хранения коллекции повторяющихся переменных.
    2013-05-01 00: 31: 18Z
  5. Спасибо за это, я использовал ваше решение в одном проекте. Я думаю, что проблема заключается в том, что: чтобы вести себя как карта, нужно возвращать значение, ранее связанное с ключом, если оно существует, но так оно и не будет. Решение, которое я использовал, - вернуть удаленное значение, если оно существует.
    2013-08-21 09: 22: 37Z

С http://www.programmersheaven.com/download/49349/download.aspx р>

private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
    Collections.sort(list, new Comparator<Object>() {
        @SuppressWarnings("unchecked")
        public int compare(Object o1, Object o2) {
            return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<>();
    for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
        Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
        result.put(entry.getKey(), entry.getValue());
    }

    return result;
}
    
182
2016-06-14 07: 51: 07Z
  1. Список, который нужно отсортировать, - это "новый LinkedList" ?? Gee. К счастью, Collections.sort () сначала выводит список в массив, чтобы избежать именно такого рода ошибок (но, тем не менее, выгрузка ArrayList в массив должна быть быстрее, чем делать то же самое для LinkedList).
    2010-04-08 13: 13: 35Z
  2. не может конвертировать из Iterator в TernaryTree.Iterator
    2011-06-03 16: 31: 15Z
  3. @ gg.kaspersky Я не говорю "плохо сортировать LinkedList", но сам LinkedList здесь плохой выбор, независимо от сортировки. Гораздо лучше использовать ArrayList, а для дополнительных точек измерьте его точно в map.size (). Также см. code.google.com/p/memory-measurer/wiki/… средняя стоимость элемента в ArrayList: 5 байт; средняя стоимость элемента в LinkedList: 24 байта. Для ArrayList точно определенного размера средняя стоимость составит 4 байта. То есть LinkedList занимает SIX количество памяти, необходимое ArrayList. Это просто раздувать
    2012-11-29 19: 29: 47Z
  4. с использованием указанных выше значений отсортировано в порядке возрастания. Как отсортировать по убыванию?
    2015-04-16 13: 09: 58Z
  5. Замените o1 и o2 для сортировки по убыванию.
    2017-06-05 20: 14: 11Z

В Java 8 вы можете использовать Streams API , чтобы сделать это значительно менее многословно:

Map<K, V> sortedMap = map.entrySet().stream()
                         .sorted(Entry.comparingByValue())
                         .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    
49
2018-09-18 06: 04: 52Z
  1. Как отсортировать его в обратном порядке?
    2014-06-01 19: 18: 17Z
  2. нашел решение - Collections.reverseOrder(comparing(Entry::getValue))
    2014-08-09 08: 26: 56Z
  3. Я думаю, что вижу там опечатку - разве «toMap» не следует называть «Collectors.toMap ()»?
    2017-10-02 14: 31: 13Z
  4. @ JakeStokes Или используйте статический импорт: -)
    2017-10-03 05: 59: 20Z
  5. Лучший способ сортировки по значению записи в обратном порядке: Entry.comparingByValue(Comparator.reverseOrder())
    2018-11-24 09: 20: 39Z

Сортировка ключей требует, чтобы Comparator просматривал каждое значение для каждого сравнения. Более масштабируемое решение будет использовать entrySet напрямую, так как тогда значение будет сразу доступно для каждого сравнения (хотя я не подкреплял это цифрами).

Вот общая версия такой вещи:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) {
    final int size = map.size();
    final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size);
    list.addAll(map.entrySet());
    final ValueComparator<V> cmp = new ValueComparator<V>();
    Collections.sort(list, cmp);
    final List<K> keys = new ArrayList<K>(size);
    for (int i = 0; i < size; i++) {
        keys.set(i, list.get(i).getKey());
    }
    return keys;
}

private static final class ValueComparator<V extends Comparable<? super V>>
                                     implements Comparator<Map.Entry<?, V>> {
    public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

Существуют способы уменьшить вращение памяти для вышеуказанного решения. Первый созданный ArrayList может, например, использоваться повторно в качестве возвращаемого значения; это потребовало бы подавления некоторых общих предупреждений, но это могло бы стоить того, чтобы повторно использовать библиотечный код. Кроме того, компаратор не нужно перераспределять при каждом вызове.

Вот более эффективная, хотя и менее привлекательная версия:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) {
    final int size = map.size();
    final List reusedList = new ArrayList(size);
    final List<Map.Entry<K, V>> meView = reusedList;
    meView.addAll(map.entrySet());
    Collections.sort(meView, SINGLE);
    final List<K> keyView = reusedList;
    for (int i = 0; i < size; i++) {
        keyView.set(i, meView.get(i).getKey());
    }
    return keyView;
}

private static final Comparator SINGLE = new ValueComparator();

Наконец, если вам нужно постоянно получать доступ к отсортированной информации (а не просто сортировать ее время от времени), вы можете использовать дополнительную мультикарту. Дайте мне знать, если вам нужно больше деталей ...

    
31
2008-10-01 21: 02: 28Z
  1. Вторая версия может быть более краткой, если вы вернете List < Map.Entry < K, V > > Это также упрощает итерацию и получение как ключей, так и значений без необходимости выполнять дополнительные операции с картой. Это все, если вы согласны с тем, что этот код небезопасен. Если карта поддержки или отсортированный список являются общими в многопоточной среде, все ставки отключены.
    2011-09-20 21: 00: 55Z

Библиотека commons-collection содержит решение под названием TreeBidiMap . Или вы можете взглянуть на API Google Collections. В нем есть TreeMultimap , который вы можно использовать.

И если вы не хотите использовать эти рамки ... они поставляются с исходным кодом.

    
26
2011-05-30 18: 59: 51Z
  1. Вам не нужно использовать коллекцию commons. Java поставляется со своим собственным java.util.TreeMap.
    2008-09-21 04: 23: 04Z
  2. да, но TreeMap гораздо менее гибок при сортировке по значению части mapentries.
    2008-09-21 06: 18: 20Z
  3. Проблема с BidiMap заключается в том, что он добавляет ограничение отношения 1: 1 между ключами и значениями для того, чтобы сделать отношение обратимым (т. е. должны быть и ключи, и значения уникальный). Это означает, что вы не можете использовать это для хранения чего-то вроде объекта подсчета слов, поскольку многие слова будут иметь одинаковое количество.
    2010-07-23 19: 49: 15Z

Я посмотрел на приведенные ответы, но многие из них сложнее, чем нужно, или удаляют элементы карты приВсе ключи имеют одинаковое значение.

Вот решение, которое, я думаю, подходит лучше:

public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) {
    Comparator<K> valueComparator =  new Comparator<K>() {
        public int compare(K k1, K k2) {
            int compare = map.get(k2).compareTo(map.get(k1));
            if (compare == 0) return 1;
            else return compare;
        }
    };
    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

Обратите внимание, что карта отсортирована по наибольшему значению и наименьшему.

    
25
2012-02-25 16: 44: 42Z
  1. ПРОБЛЕМА: если вы хотите использовать возвращенную карту позже, например, чтобы проверить, содержит ли она определенный элемент, вы всегда получите false из-за вашего собственного компаратора! Возможное решение: заменить последнюю строку следующим: вернуть новый LinkedHashMap < K, V > (sortedByValues);
    2011-10-02 15: 58: 30Z
  2. Для меня это выглядит чистым решением, за исключением того факта, что @ErelSegalHalevi указал, что проверка значений на карте невозможна, так как вы указали компаратор. map.put («1», «One»); map.put ("2", "Two"); map.put («3», «Три»); map.put («4», «Четыре»); map.put ("5", "Five"); map.containsKey ("1") всегда будет возвращать false, если вы возвращаете новый объект в функции sortByValues ​​(), как return new TreeMap < K, V > (sortedByValues); решает проблему. Спасибо Абхи
    2013-05-14 08: 14: 40Z
  3. в значительной степени совпадает с ответом пользователя 157196 и Картера Пейджа. Страница Картера содержит исправление LinkedHashMap
    2014-04-22 17: 37: 16Z
  4. 4-я строка решения должна быть int Сравнить = map.get (k1) .compareTo (map.get (k2)); если вам нужно по возрастанию
    2014-07-22 08: 20: 05Z

Для достижения этой цели с помощью новых функций в Java 8:

import static java.util.Map.Entry.comparingByValue;
import static java.util.stream.Collectors.toList;

<K, V> List<Entry<K, V>> sort(Map<K, V> map, Comparator<? super V> comparator) {
    return map.entrySet().stream().sorted(comparingByValue(comparator)).collect(toList());
}

Записи упорядочены по их значениям с использованием данного компаратора. В качестве альтернативы, если ваши значения взаимно сопоставимы, явный компаратор не требуется:

<K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
    return map.entrySet().stream().sorted(comparingByValue()).collect(toList());
}

Возвращенный список является снимком данной карты во время вызова этого метода, поэтому ни один из них не отразит последующие изменения другого. Для живого итеративного просмотра карты:

<K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) {
    return () -> map.entrySet().stream().sorted(comparingByValue()).iterator();
}

Возвращаемая итерация создает новый снимок данной карты при каждой итерации, поэтому, за исключением одновременного изменения, она всегда будет отражать текущее состояние карты.

    
17
2014-06-24 22: 16: 19Z
  1. Возвращает список записей, а не карту, отсортированную по значению. Другая версия, которая возвращает карту: stackoverflow.com/a/22132422/829571
    2014-03-03 17: 26: 05Z

Создайте собственный компаратор и используйте его при создании нового объекта TreeMap.

class MyComparator implements Comparator<Object> {

    Map<String, Integer> map;

    public MyComparator(Map<String, Integer> map) {
        this.map = map;
    }

    public int compare(Object o1, Object o2) {

        if (map.get(o2) == map.get(o1))
            return 1;
        else
            return ((Integer) map.get(o2)).compareTo((Integer)     
                                                            map.get(o1));

    }
}

Используйте приведенный ниже код в своей основной функции

    Map<String, Integer> lMap = new HashMap<String, Integer>();
    lMap.put("A", 35);
    lMap.put("B", 75);
    lMap.put("C", 50);
    lMap.put("D", 50);

    MyComparator comparator = new MyComparator(lMap);

    Map<String, Integer> newMap = new TreeMap<String, Integer>(comparator);
    newMap.putAll(lMap);
    System.out.println(newMap);

Вывод:

{B=75, D=50, C=50, A=35}
    
15
2013-02-10 06: 10: 09Z
  1. Это также работает для дублированных значений !!!
    2013-02-10 06: 12: 12Z
  2. В случае, если значения равны, я изменил строку «return 1» для сравнения ключей: «return ((String) o1) .compareTo ((String)» o2); "
    2019-03-17 17: 57: 49Z

Хотя я согласен с тем, что постоянная необходимость сортировать карту, вероятно, является запахом, я думаю, что следующий код - это самый простой способ сделать это без использования другой структуры данных.

public class MapUtilities {

public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
    Collections.sort(entries, new ByValue<K, V>());
    return entries;
}

private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
    public int compare(Entry<K, V> o1, Entry<K, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

} р>

А вот смущающий неполный юнит-тест:

public class MapUtilitiesTest extends TestCase {
public void testSorting() {
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("One", 1);
    map.put("Two", 2);
    map.put("Three", 3);

    List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
    assertEquals("First", "One", sorted.get(0).getKey());
    assertEquals("Second", "Two", sorted.get(1).getKey());
    assertEquals("Third", "Three", sorted.get(2).getKey());
}

} р>

Результатом является отсортированный список объектов Map.Entry, из которого можно получить ключи и значения.

    
14
2008-09-24 00: 58: 27Z
  1. Этот метод намного проще и более интуитивно понятен, чем создание карты < V, List < K > > объект с почти таким же эффектом. Значения не должны быть ключами в объекте Map, в действительности вам нужен список в этой ситуации, ИМХО.
    2011-12-30 01: 49: 48Z
  2. Это решение не работает с большим количеством значений, оно связано с моими подсчетами (значением, связанным с каждым ключом)
    2012-09-15 16: 17: 33Z
  3. Это странно. Не могли бы вы уточнить? Каким был ваш вывод и какой выход вы ожидали?
    2012-09-29 10: 24: 53Z

Ответ, за который проголосовали большинство, не работает, если у вас есть 2 равных элемента. TreeMap оставляет равные значения.

пример: несортированная карта

key/value: D/67.3
key/value: A/99.5
key/value: B/67.4
key/value: C/67.5
key/value: E/99.5

Результаты р>

key/value: A/99.5
key/value: C/67.5
key/value: B/67.4
key/value: D/67.3

Так что опускай E !!

Для меня это работало нормально, чтобы настроить компаратор, если он равен не вернуть 0, а -1.

в примере:

  
    

класс ValueComparator реализует Comparator {

         

База карт;       public ValueComparator (База карт) {           this.base = base;       }

         

public int Compare (Объект a, Объект b) {

if((Double)base.get(a) < (Double)base.get(b)) {
  return 1;
} else if((Double)base.get(a) == (Double)base.get(b)) {
  return -1;
} else {
  return -1;
}
         

}     }

  

теперь он возвращает:

несортированная карта:

key/value: D/67.3
key/value: A/99.5
key/value: B/67.4
key/value: C/67.5
key/value: E/99.5

результаты:

key/value: A/99.5
key/value: E/99.5
key/value: C/67.5
key/value: B/67.4
key/value: D/67.3

как ответ иностранцам (2011 ноябрь 22): Я использую это решение для карты целочисленных идентификаторов и имен, но идея та же самая, поэтому, возможно, приведенный выше код неверен (я напишу его в тесте и дам правильный код), это код для сортировки карт, основанной на решении выше:

package nl.iamit.util;

import java.util.Comparator;
import java.util.Map;

public class Comparators {


    public static class MapIntegerStringComparator implements Comparator {

        Map<Integer, String> base;

        public MapIntegerStringComparator(Map<Integer, String> base) {
            this.base = base;
        }

        public int compare(Object a, Object b) {

            int compare = ((String) base.get(a))
                    .compareTo((String) base.get(b));
            if (compare == 0) {
                return -1;
            }
            return compare;
        }
    }


}

и это тестовый класс (я только что проверил его, и это работает для Integer, String Map:

package test.nl.iamit.util;

import java.util.HashMap;
import java.util.TreeMap;
import nl.iamit.util.Comparators;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;

public class TestComparators {


    @Test
    public void testMapIntegerStringComparator(){
        HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>();
        Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator(
                unSoretedMap);
        TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc);
        //the testdata:
        unSoretedMap.put(new Integer(1), "E");
        unSoretedMap.put(new Integer(2), "A");
        unSoretedMap.put(new Integer(3), "E");
        unSoretedMap.put(new Integer(4), "B");
        unSoretedMap.put(new Integer(5), "F");

        sorted_map.putAll(unSoretedMap);

        Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) };
        Object[] currecntKeys=sorted_map.keySet().toArray();

        assertArrayEquals(targetKeys,currecntKeys);
    }
}

вот код для компаратора карты:

public static class MapStringDoubleComparator implements Comparator {

    Map<String, Double> base;

    public MapStringDoubleComparator(Map<String, Double> base) {
        this.base = base;
    }

    //note if you want decending in stead of ascending, turn around 1 and -1
    public int compare(Object a, Object b) {
        if ((Double) base.get(a) == (Double) base.get(b)) {
            return 0;
        } else if((Double) base.get(a) < (Double) base.get(b)) {
            return -1;
        }else{
            return 1;
        }
    }
}

и это тест для этого:

@Test
public void testMapStringDoubleComparator(){
    HashMap<String, Double> unSoretedMap = new HashMap<String, Double>();
    Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator(
            unSoretedMap);
    TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
    //the testdata:
    unSoretedMap.put("D",new Double(67.3));
    unSoretedMap.put("A",new Double(99.5));
    unSoretedMap.put("B",new Double(67.4));
    unSoretedMap.put("C",new Double(67.5));
    unSoretedMap.put("E",new Double(99.5));

    sorted_map.putAll(unSoretedMap);

    Object[] targetKeys={"D","B","C","E","A"};
    Object[] currecntKeys=sorted_map.keySet().toArray();

    assertArrayEquals(targetKeys,currecntKeys);
}

Конечно, вы можете сделать это намного более общим, но мне просто нужно было это для 1 случая (Карта)

    
11
2011-11-23 13: 11: 46Z
  1. Это не работает для меня. Я получаю все значения как ноль.
    2011-11-22 00: 42: 43Z
  2. вы были правы, в коде, который я дал сначала, произошла ошибка! Я надеюсь, что мое недавнее редактирование поможет вам.
    2011-11-23 13: 12: 40Z

Используйте общий компаратор, например:

final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> {

    private Map<K,V> map;

    private MapValueComparator() {
        super();
    }

    public MapValueComparator(Map<K,V> map) {
        this();
        this.map = map;
    }

    public int compare(K o1, K o2) {
        return map.get(o1).compareTo(map.get(o2));
    }
}
    
11
2014-09-09 06: 43: 09Z

Вместо использования Collections.sort, как некоторые, я бы предложил использовать Arrays.sort. Фактически, то, что делает Collections.sort, выглядит примерно так:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    Object[] a = list.toArray();
    Arrays.sort(a);
    ListIterator<T> i = list.listIterator();
    for (int j=0; j<a.length; j++) {
        i.next();
        i.set((T)a[j]);
    }
}

Он просто вызывает toArray в списке и затем использует Arrays.sort. Таким образом, все записи карты будут скопированы три раза: один раз из карты во временный список (будь то LinkedList или ArrayList), затем во временный массив и наконец, на новую карту.

Мое решение пропускает этот один шаг, поскольку оно не создает ненужный LinkedList. Вот код, универсальный и производительныйоптимален:

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) 
{
    @SuppressWarnings("unchecked")
    Map.Entry<K,V>[] array = map.entrySet().toArray(new Map.Entry[map.size()]);

    Arrays.sort(array, new Comparator<Map.Entry<K, V>>() 
    {
        public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) 
        {
            return e1.getValue().compareTo(e2.getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<K, V>();
    for (Map.Entry<K, V> entry : array)
        result.put(entry.getKey(), entry.getValue());

    return result;
}
    
9
2012-05-30 00: 47: 27Z

Это вариант ответа Энтони, который не работает, если есть повторяющиеся значения:

public static <K, V extends Comparable<V>> Map<K, V> sortMapByValues(final Map<K, V> map) {
    Comparator<K> valueComparator =  new Comparator<K>() {
        public int compare(K k1, K k2) {
            final V v1 = map.get(k1);
            final V v2 = map.get(k2);

            /* Not sure how to handle nulls ... */
            if (v1 == null) {
                return (v2 == null) ? 0 : 1;
            }

            int compare = v2.compareTo(v1);
            if (compare != 0)
            {
                return compare;
            }
            else
            {
                Integer h1 = k1.hashCode();
                Integer h2 = k2.hashCode();
                return h2.compareTo(h1);
            }
        }
    };
    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

Обратите внимание, что это довольно непросто, как обрабатывать нули.

Одним из важных преимуществ этого подхода является то, что он на самом деле возвращает карту, в отличие от некоторых других предлагаемых здесь решений.

    
8
2011-02-09 10: 21: 25Z
  1. Это неправильно, мой метод работает, если есть повторяющиеся значения. Я использовал его с картами, имеющими более 100 ключей со значением «1».
    2012-02-25 16: 40: 53Z

Основная проблема. Если вы используете первый ответ (Google берет вас здесь), измените компаратор, чтобы добавить предложение равенства, иначе вы не сможете получить значения из sorted_map по ключам:

public int compare(String a, String b) {
        if (base.get(a) > base.get(b)) {
            return 1;
        } else if (base.get(a) < base.get(b)){
            return -1;
        } 

        return 0;
        // returning 0 would merge keys
    }
    
7
2012-11-18 06: 37: 50Z
  1. Теперь, когда вы добавляете две записи с одинаковыми значениями, они будут объединены, вы должны возвращать только 0, если уверены, что объекты одинаковы (равны)
    2013-01-23 04: 54: 00Z

Лучший подход

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry; 

public class OrderByValue {

  public static void main(String a[]){
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("java", 20);
    map.put("C++", 45);
    map.put("Unix", 67);
    map.put("MAC", 26);
    map.put("Why this kolavari", 93);
    Set<Entry<String, Integer>> set = map.entrySet();
    List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
    Collections.sort( list, new Comparator<Map.Entry<String, Integer>>()
    {
        public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 )
        {
            return (o1.getValue()).compareTo( o2.getValue() );//Ascending order
            //return (o2.getValue()).compareTo( o1.getValue() );//Descending order
        }
    } );
    for(Map.Entry<String, Integer> entry:list){
        System.out.println(entry.getKey()+" ==== "+entry.getValue());
    }
  }}

Выход

java ==== 20

MAC ==== 26

C++ ==== 45

Unix ==== 67

Why this kolavari ==== 93
    
7
2016-12-05 22: 29: 17Z

На этот вопрос уже есть много ответов, но ни один из них не дал мне того, что я искал, реализацию карты, которая возвращает ключи и записи, отсортированные по связанному значению, и поддерживает это свойство, так как ключи и значения изменяются в карта. Два другие вопросы Спроси об этом конкретно.

Я подготовил общий дружественный пример, который решает этот вариант использования. Эта реализация не соблюдает все контракты интерфейса Map, такие как отражение изменений и удалений значений в наборах, возвращаемых из keySet () и entrySet () в исходном объекте. Я чувствовал, что такое решение будет слишком большим, чтобы включить его в ответ на переполнение стека. Если мне удастся создать более полную реализацию, возможно, я опубликую ее на Github, а затем на ссылку в обновленной версии этого ответа.

import java.util.*;

/**
 * A map where {@link #keySet()} and {@link #entrySet()} return sets ordered
 * by associated values based on the the comparator provided at construction
 * time. The order of two or more keys with identical values is not defined.
 * <p>
 * Several contracts of the Map interface are not satisfied by this minimal
 * implementation.
 */
public class ValueSortedMap<K, V> extends HashMap<K, V> {
    protected Map<V, Collection<K>> valueToKeysMap;

    // uses natural order of value object, if any
    public ValueSortedMap() {
        this((Comparator<? super V>) null);
    }

    public ValueSortedMap(Comparator<? super V> valueComparator) {
        this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
    }

    public boolean containsValue(Object o) {
        return valueToKeysMap.containsKey(o);
    }

    public V put(K k, V v) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        super.put(k, v);
        if (!valueToKeysMap.containsKey(v)) {
            Collection<K> keys = new ArrayList<K>();
            keys.add(k);
            valueToKeysMap.put(v, keys);
        } else {
            valueToKeysMap.get(v).add(k);
        }
        return oldV;
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    public V remove(Object k) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            super.remove(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        return oldV;
    }

    public void clear() {
        super.clear();
        valueToKeysMap.clear();
    }

    public Set<K> keySet() {
        LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
        for (V v : valueToKeysMap.keySet()) {
            Collection<K> keys = valueToKeysMap.get(v);
            ret.addAll(keys);
        }
        return ret;
    }

    public Set<Map.Entry<K, V>> entrySet() {
        LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
        for (Collection<K> keys : valueToKeysMap.values()) {
            for (final K k : keys) {
                final V v = get(k);
                ret.add(new Map.Entry<K,V>() {
                    public K getKey() {
                        return k;
                    }

                    public V getValue() {
                        return v;
                    }

                    public V setValue(V v) {
                        throw new UnsupportedOperationException();
                    }
                });
            }
        }
        return ret;
    }
}
    
6
2017-06-05 21: 36: 27Z
  1. Если Comparable и Comparator не разрешены, то как это сделать?
    2019-05-28 17: 41: 18Z
  2. Не уверен, что я понимаю ваш вариант использования, может быть, вы можете уточнить. Если объект, который вы хотите использовать в качестве значения, не является сопоставимым, вам необходимо преобразовать его в объект, который является
    .
    2019-05-30 04: 57: 22Z

В зависимости от контекста используется java.util.LinkedHashMap<T>, который запоминает порядок, в котором элементы размещаются на карте. В противном случае, если вам нужно отсортировать значения на основе их естественного порядка, я бы порекомендовал вести отдельный список, который можно отсортировать с помощью Collections.sort().

    
5
2008-09-20 21: 07: 49Z
  1. Я не понимаю, почему это было -1, до сих пор LinkedHashMap, вероятно, является лучшим решением для меня, я просто пытаюсь выяснить, насколько дорого это сделать выбросить и создать новый LinkedHashMap.
    2016-04-18 20: 07: 15Z

Поскольку TreeMap < > не работает для значений, которые могут быть равны, я использовал это:

private <K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map)     {
    List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
    Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
        public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
            return o1.getValue().compareTo(o2.getValue());
        }
    });

    return list;
}

Возможно, вы захотите поместить список в LinkedHashMap , но если вы собираетесь только выполнять итерацию над этим сразу, это лишнее ...

    
5
2011-08-31 20: 40: 25Z
  1. верно, но ваш компаратор не обрабатывает регистр равных значений
    2011-12-27 17: 48: 33Z

Это слишком сложно. Карты не должны были выполнять такую ​​работу, как сортировка их по значению. Самый простой способ - создать свой собственный класс, чтобы он соответствовал вашим требованиям.

В примере ниже вы должны добавить TreeMap компаратор в место, где * находится. Но с помощью API Java он дает компаратору только ключи, а не значения. Все приведенные здесь примеры основаны на 2 картах. Один хэш и одно новое дерево. Что странно.

Пример:

Map<Driver driver, Float time> map = new TreeMap<Driver driver, Float time>(*);

Так что измените карту в набор следующим образом:

ResultComparator rc = new ResultComparator();
Set<Results> set = new TreeSet<Results>(rc);

Вы создадите класс Results,

public class Results {
    private Driver driver;
    private Float time;

    public Results(Driver driver, Float time) {
        this.driver = driver;
        this.time = time;
    }

    public Float getTime() {
        return time;
    }

    public void setTime(Float time) {
        this.time = time;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver (Driver driver) {
        this.driver = driver;
    }
}

и класс Comparator:

public class ResultsComparator implements Comparator<Results> {
    public int compare(Results t, Results t1) {
        if (t.getTime() < t1.getTime()) {
            return 1;
        } else if (t.getTime() == t1.getTime()) {
            return 0;
        } else {
            return -1;
        }
    }
}

Таким образом, вы можете легко добавить больше зависимостей.

И в качестве последнего пункта я добавлю простой итератор:

Iterator it = set.iterator();
while (it.hasNext()) {
    Results r = (Results)it.next();
    System.out.println( r.getDriver().toString
        //or whatever that is related to Driver class -getName() getSurname()
        + " "
        + r.getTime()
        );
}
    
5
2011-12-29 21: 33: 51Z

Основан на коде @devinmoore, методах сортировки карт, использующих обобщенные типы и поддерживающих как возрастание, так и убывание.

/**
 * Sort a map by it's keys in ascending order. 
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map) {
    return sortMapByKey(map, SortingOrder.ASCENDING);
}

/**
 * Sort a map by it's values in ascending order.
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map) {
    return sortMapByValue(map, SortingOrder.ASCENDING);
}

/**
 * Sort a map by it's keys.
 *  
 * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. 
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map, final SortingOrder sortingOrder) {
    Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return comparableCompare(o1.getKey(), o2.getKey(), sortingOrder);
        }
    };

    return sortMap(map, comparator);
}

/**
 * Sort a map by it's values.
 *  
 * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. 
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map, final SortingOrder sortingOrder) {
    Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return comparableCompare(o1.getValue(), o2.getValue(), sortingOrder);
        }
    };

    return sortMap(map, comparator);
}

@SuppressWarnings("unchecked")
private static <T> int comparableCompare(T o1, T o2, SortingOrder sortingOrder) {
    int compare = ((Comparable<T>)o1).compareTo(o2);

    switch (sortingOrder) {
    case ASCENDING:
        return compare;
    case DESCENDING:
        return (-1) * compare;
    }

    return 0;
}

/**
 * Sort a map by supplied comparator logic.
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMap(final Map<K, V> map, final Comparator<Map.Entry<K, V>> comparator) {
    // Convert the map into a list of key,value pairs.
    List<Map.Entry<K, V>> mapEntries = new LinkedList<Map.Entry<K, V>>(map.entrySet());

    // Sort the converted list according to supplied comparator.
    Collections.sort(mapEntries, comparator);

    // Build a new ordered map, containing the same entries as the old map.  
    LinkedHashMap<K, V> result = new LinkedHashMap<K, V>(map.size() + (map.size() / 20));
    for(Map.Entry<K, V> entry : mapEntries) {
        // We iterate on the mapEntries list which is sorted by the comparator putting new entries into 
        // the targeted result which is a sorted map. 
        result.put(entry.getKey(), entry.getValue());
    }

    return result;
}

/**
 * Sorting order enum, specifying request result sort behavior.
 * @author Maxim Veksler
 *
 */
public static enum SortingOrder {
    /**
     * Resulting sort will be from smaller to biggest.
     */
    ASCENDING,
    /**
     * Resulting sort will be from biggest to smallest.
     */
    DESCENDING
}
    
4
2009-04-14 13: 44: 29Z
  1. С другой стороны, возможно, лучшим решением было бы просто использовать самосортирующуюся карту, в случае использования org.apache.commons.collections.bidimap.TreeBidiMap
    2009-04-14 13: 48: 20Z

Вот решение OO (т.е. не использует методы static):

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class SortableValueMap<K, V extends Comparable<V>>
  extends LinkedHashMap<K, V> {
  public SortableValueMap() { }

  public SortableValueMap( Map<K, V> map ) {
    super( map );
  }

  public void sortByValue() {
    List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>( entrySet() );

    Collections.sort( list, new Comparator<Map.Entry<K, V>>() {
      public int compare( Map.Entry<K, V> entry1, Map.Entry<K, V> entry2 ) {
        return entry1.getValue().compareTo( entry2.getValue() );
      }
    });

    clear();

    for( Map.Entry<K, V> entry : list ) {
      put( entry.getKey(), entry.getValue() );
    }
  }

  private static void print( String text, Map<String, Double> map ) {
    System.out.println( text );

    for( String key : map.keySet() ) {
      System.out.println( "key/value: " + key + "/" + map.get( key ) );
    }
  }

  public static void main( String[] args ) {
    SortableValueMap<String, Double> map =
      new SortableValueMap<String, Double>();

    map.put( "A", 67.5 );
    map.put( "B", 99.5 );
    map.put( "C", 82.4 );
    map.put( "D", 42.0 );

    print( "Unsorted map", map );
    map.sortByValue();
    print( "Sorted map", map );
  }
}

Настоящим пожертвовано в общественное достояние.

    
4
2011-01-02 05: 53: 22Z

Afaik - самый чистый способ использования коллекций для сортировки карты по значению:

Map<String, Long> map = new HashMap<String, Long>();
// populate with data to sort on Value
// use datastructure designed for sorting

Queue queue = new PriorityQueue( map.size(), new MapComparable() );
queue.addAll( map.entrySet() );

// get a sorted map
LinkedHashMap<String, Long> linkedMap = new LinkedHashMap<String, Long>();

for (Map.Entry<String, Long> entry; (entry = queue.poll())!=null;) {
    linkedMap.put(entry.getKey(), entry.getValue());
}

public static class MapComparable implements Comparator<Map.Entry<String, Long>>{

  public int compare(Entry<String, Long> e1, Entry<String, Long> e2) {
    return e1.getValue().compareTo(e2.getValue());
  }
}
    
4
2011-06-08 15: 23: 04Z

Несколько простых изменений, чтобы получить отсортированную карту с парами, которые имеют повторяющиеся значения. В методе сравнения (класс ValueComparator), когда значения равны, не возвращают 0, а возвращают результат сравнения двух ключей. Ключи различны на карте, поэтому вам удастся сохранить дубликаты значений (которые, кстати, отсортированы по ключам). Таким образом, приведенный выше пример можно изменить следующим образом:

    public int compare(Object a, Object b) {

        if((Double)base.get(a) < (Double)base.get(b)) {
          return 1;
        } else if((Double)base.get(a) == (Double)base.get(b)) {
          return ((String)a).compareTo((String)b);
        } else {
          return -1;
        }
      }
    }
4
2011-09-01 09: 53: 33Z

Конечно, решение Стивена действительно великолепно, но для тех, кто не может использовать гуаву:

Вот мое решение для сортировки по значению карты. Это решение обрабатывает случай, когда есть два одинаковых значения и т. Д.

// If you want to sort a map by value, and if there can be twice the same value:

// here is your original map
Map<String,Integer> mapToSortByValue = new HashMap<String, Integer>();
mapToSortByValue.put("A", 3);
mapToSortByValue.put("B", 1);
mapToSortByValue.put("C", 3);
mapToSortByValue.put("D", 5);
mapToSortByValue.put("E", -1);
mapToSortByValue.put("F", 1000);
mapToSortByValue.put("G", 79);
mapToSortByValue.put("H", 15);

// Sort all the map entries by value
Set<Map.Entry<String,Integer>> set = new TreeSet<Map.Entry<String,Integer>>(
        new Comparator<Map.Entry<String,Integer>>(){
            @Override
            public int compare(Map.Entry<String,Integer> obj1, Map.Entry<String,Integer> obj2) {
                Integer val1 = obj1.getValue();
                Integer val2 = obj2.getValue();
                // DUPLICATE VALUE CASE
                // If the values are equals, we can't return 0 because the 2 entries would be considered
                // as equals and one of them would be deleted (because we use a set, no duplicate, remember!)
                int compareValues = val1.compareTo(val2);
                if ( compareValues == 0 ) {
                    String key1 = obj1.getKey();
                    String key2 = obj2.getKey();
                    int compareKeys = key1.compareTo(key2);
                    if ( compareKeys == 0 ) {
                        // what you return here will tell us if you keep REAL KEY-VALUE duplicates in your set
                        // if you want to, do whatever you want but do not return 0 (but don't break the comparator contract!)
                        return 0;
                    }
                    return compareKeys;
                }
                return compareValues;
            }
        }
);
set.addAll(mapToSortByValue.entrySet());


// OK NOW OUR SET IS SORTED COOL!!!!

// And there's nothing more to do: the entries are sorted by value!
for ( Map.Entry<String,Integer> entry : set ) {
    System.out.println("Set entries: " + entry.getKey() + " -> " + entry.getValue());
}




// But if you add them to an hashmap
Map<String,Integer> myMap = new HashMap<String,Integer>();
// When iterating over the set the order is still good in the println...
for ( Map.Entry<String,Integer> entry : set ) {
    System.out.println("Added to result map entries: " + entry.getKey() + " " + entry.getValue());
    myMap.put(entry.getKey(), entry.getValue());
}

// But once they are in the hashmap, the order is not kept!
for ( Integer value : myMap.values() ) {
    System.out.println("Result map values: " + value);
}
// Also this way doesn't work:
// Logic because the entryset is a hashset for hashmaps and not a treeset
// (and even if it was a treeset, it would be on the keys only)
for ( Map.Entry<String,Integer> entry : myMap.entrySet() ) {
    System.out.println("Result map entries: " + entry.getKey() + " -> " + entry.getValue());
}


// CONCLUSION:
// If you want to iterate on a map ordered by value, you need to remember:
// 1) Maps are only sorted by keys, so you can't sort them directly by value
// 2) So you simply CAN'T return a map to a sortMapByValue function
// 3) You can't reverse the keys and the values because you have duplicate values
//    This also means you can't neither use Guava/Commons bidirectionnal treemaps or stuff like that

// SOLUTIONS
// So you can:
// 1) only sort the values which is easy, but you loose the key/value link (since you have duplicate values)
// 2) sort the map entries, but don't forget to handle the duplicate value case (like i did)
// 3) if you really need to return a map, use a LinkedHashMap which keep the insertion order

Исполнитель: http://www.ideone.com/dq3Lu

Вывод:

Set entries: E -> -1
Set entries: B -> 1
Set entries: A -> 3
Set entries: C -> 3
Set entries: D -> 5
Set entries: H -> 15
Set entries: G -> 79
Set entries: F -> 1000
Added to result map entries: E -1
Added to result map entries: B 1
Added to result map entries: A 3
Added to result map entries: C 3
Added to result map entries: D 5
Added to result map entries: H 15
Added to result map entries: G 79
Added to result map entries: F 1000
Result map values: 5
Result map values: -1
Result map values: 1000
Result map values: 79
Result map values: 3
Result map values: 1
Result map values: 3
Result map values: 15
Result map entries: D -> 5
Result map entries: E -> -1
Result map entries: F -> 1000
Result map entries: G -> 79
Result map entries: A -> 3
Result map entries: B -> 1
Result map entries: C -> 3
Result map entries: H -> 15

Надеюсь, это поможет некоторым людям

    
4
2011-12-27 17: 43: 44Z

Поздний вход.

С появлением Java-8 мы можем использовать потоки для манипулирования данными очень простым и лаконичным способом. Вы можете использовать потоки для сортировки записей карты по значению и создать LinkedHashMap , которая сохраняет итерацию в порядке вставки .

Например:

LinkedHashMap sortedByValueMap = map.entrySet().stream()
                .sorted(comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey))     //first sorting by Value, then sorting by Key(entries with same value)
                .collect(LinkedHashMap::new,(map,entry) -> map.put(entry.getKey(),entry.getValue()),LinkedHashMap::putAll);

Для обратного упорядочения замените:

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)

с р>

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()
    
4
2018-05-18 08: 50: 04Z
  1. Спасибо за эту закомментированную версию. Один вопрос: в чем разница с использованием Entry.comparingByValue() (как указано выше в ассилийском вопросе stackoverflow.com/a/22132422/1480587 ) или comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey) что ты использовал? Я так понимаю, вы также сравниваете ключи, если значения идентичны, верно? Я заметил, что сортировка поддерживает порядок элементов с одинаковым значением - так нужна ли сортировка по ключам, если ключи уже были отсортированы раньше?
    2019-01-11 08: 58: 24Z

Если у вас есть дубликаты ключей и только небольшой набор данных (< 1000) и ваш код не критичен к производительности, вы можете просто сделать следующее:

Map<String,Integer> tempMap=new HashMap<String,Integer>(inputUnsortedMap);
LinkedHashMap<String,Integer> sortedOutputMap=new LinkedHashMap<String,Integer>();

for(int i=0;i<inputUnsortedMap.size();i++){
    Map.Entry<String,Integer> maxEntry=null;
    Integer maxValue=-1;
    for(Map.Entry<String,Integer> entry:tempMap.entrySet()){
        if(entry.getValue()>maxValue){
            maxValue=entry.getValue();
            maxEntry=entry;
        }
    }
    tempMap.remove(maxEntry.getKey());
    sortedOutputMap.put(maxEntry.getKey(),maxEntry.getValue());
}

inputUnsortedMap - это ввод кода.

Переменная sortedOutputMap будет содержать данные в порядке убывания при повторном выполнении. Чтобы изменить порядок, просто измените > на < в операторе if.

Не самая быстрая сортировка, но выполняет работу без каких-либо дополнительных зависимостей.

    
3
2011-09-26 21: 39: 32Z

Я объединил решения пользователя 157196 и Картера Пейджа:

class MapUtil {

    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue( Map<K, V> map ){
        ValueComparator<K,V> bvc =  new ValueComparator<K,V>(map);
        TreeMap<K,V> sorted_map = new TreeMap<K,V>(bvc);
        sorted_map.putAll(map);
        return sorted_map;
    }

}

class ValueComparator<K, V extends Comparable<? super V>> implements Comparator<K> {

    Map<K, V> base;
    public ValueComparator(Map<K, V> base) {
        this.base = base;
    }

    public int compare(K a, K b) {
        int result = (base.get(a).compareTo(base.get(b)));
        if (result == 0) result=1;
        // returning 0 would merge keys
        return result;
    }
}
    
3
2014-04-22 09: 09: 59Z
источник размещен Вот