Сортировать карту <ключ, значение> по значениям

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

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

Есть способ попроще?

Карта не предназначена для сортировки, но к ней можно быстро получить доступ. Равные значения объектов нарушают ограничение карты. Используйте набор записей, например List<Map.Entry<...>> list =new LinkedList(map.entrySet()) и Collections.sort .....

Hannes 09.02.2014 21:34

Случай, когда это может возникнуть, когда мы пытаемся использовать счетчик в Java (Map <Object, Integer>). Тогда сортировка по количеству вхождений будет обычной операцией. Такой язык, как Python, имеет встроенную структуру данных Counter. Для альтернативного способа реализации на Java, здесь является примером

demongolem 21.12.2017 23:03

Существует множество вариантов использования отсортированных карт, поэтому у вас есть TreeMap и ConcurrentSkipListMap в jdk.

alobodzk 22.04.2018 22:10

См. Также stackoverflow.com/questions/7860822/…

Raedwald 23.07.2019 13:37

TreeMap и ConcurrentSkipListMap сортируются по ключу. Вопрос в сортировке по значению.

Peter 28.04.2020 15:28
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1 712
5
1 469 893
59

Ответы 59

От 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;
}

Список для сортировки - «новый LinkedList» ?? Ну и дела. К счастью, Collections.sort () сначала выгружает список в массив, чтобы избежать именно такой ошибки (но все же сброс ArrayList в массив должен быть быстрее, чем то же самое для LinkedList).

Dimitris Andreou 08.04.2010 17:13

не может преобразовать Iterator в TernaryTree.Iterator

lisak 03.06.2011 20:31

зачем использовать Collections.sort, если есть Arrays.sort? см. мой ответ для более подробной информации

ciamej 30.05.2012 04:49

@DimitrisAndreou, почему плохо сортировать связанный список?

kaspersky 28.11.2012 15:15

@ gg.kaspersky Я не говорю, что «сортировать LinkedList - плохо», но сам LinkedList - плохой выбор, независимо от сортировки. Много лучше использовать ArrayList, а для дополнительных точек размер точно равен map.size (). Также см. Среднюю стоимость code.google.com/p/memory-measurer/wiki/… на элемент в ArrayList: 5 байтов, средняя стоимость на элемент в LinkedList: 24 байта. Для ArrayList точного размера средняя стоимость составит 4 байта. То есть LinkedList занимает ШЕСТЬ, умноженное на объем памяти, необходимый для ArrayList. Это просто раздувание

Dimitris Andreou 29.11.2012 23:29

Показывает ошибку: метод sort (List <T>, Comparator <? Super T>) в типе Collections не применим для аргументов (List, new Comparator () {})

Rishi Dua 26.06.2014 07:33

^ Извините за это. Я не импортировал java.util.comparator

Rishi Dua 26.06.2014 07:34

с использованием приведенных выше значений были отсортированы в порядке возрастания. Как сортировать по убыванию?

ram 16.04.2015 16:09

Замените o1 и o2 для сортировки по убыванию.

Soheil 05.06.2017 23:14

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

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

NobleUplift 18.04.2016 23:07

Библиотека общих коллекций содержит решение под названием ДеревоБидиКарта. Или вы можете взглянуть на API коллекций Google. У него есть Дерево, который вы можете использовать.

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

Вам не обязательно использовать коллекцию общих ресурсов. Java поставляется с собственным java.util.TreeMap.

yoliho 21.09.2008 08:23

да, но TreeMap гораздо менее гибок при сортировке по части ценить элементов карты.

p3t0r 21.09.2008 10:18

Проблема с BidiMap заключается в том, что он добавляет ограничение отношения 1: 1 между ключами и значениями, чтобы сделать отношение обратимым (т. Е. И ключи, и значения должны быть уникальными). Это означает, что вы не можете использовать это для хранения чего-то вроде объекта подсчета слов, поскольку у многих слов будет одинаковое количество слов.

Doug 23.07.2010 23:49

Если ваши значения карты реализуют Comparable (например, String), это должно работать

Map<Object, String> map = new HashMap<Object, String>();
// Populate the Map
List<String> mapValues = new ArrayList<String>(map.values());
Collections.sort(mapValues);

Если сами значения карты не реализуют Comparable, но у вас есть экземпляр Comparable, который может их сортировать, замените последнюю строку следующим образом:

Collections.sort(mapValues, comparable);

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

Aaron 21.09.2008 23:27

Причина в том, что это не решает проблемы. Значения сортируются нормально, но ключи выбрасываются. Был задан вопрос о способе сортировки карты, а это означает, что ключи и значения все еще должны быть связаны.

gregory 20.10.2008 17:22

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

whiskeysierra 10.01.2010 17:05

Для сортировки по ключам я нашел лучшее решение с помощью TreeMap (я также постараюсь получить готовое решение для сортировки на основе значений):

public static void main(String[] args) {
    Map<String, String> unsorted = new HashMap<String, String>();
    unsorted.put("Cde", "Cde_Value");
    unsorted.put("Abc", "Abc_Value");
    unsorted.put("Bcd", "Bcd_Value");

    Comparator<String> comparer = new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }};

    Map<String, String> sorted = new TreeMap<String, String>(comparer);
    sorted.putAll(unsorted);
    System.out.println(sorted);
}

Результат будет:

{Abc = Abc_Value, Bcd = Bcd_Value, Cde = Cde_Value}

поместите все значения в treeMap автоматически, они будут отсортированы по ключу с помощью R-B Tree.

sitakant 15.12.2015 14:19

Компаратор, переданный конструктору TreeMap, сравнивает КЛЮЧИ TreeMap вместо ЗНАЧЕНИЙ. В вашем примере это сработало, потому что сортировка по ключам такая же, как сортировка по значениям. Полный конструктор: public TreeMap (Comparator <? Super K> comparator) Где он принимает потомков K, который является ключом. Для получения дополнительной информации см .: docs.oracle.com/javase/6/docs/api/java/util/…

Hussein El Motayam 06.07.2017 14:41

Используйте java.util.TreeMap.

«Карта сортируется в соответствии с естественным порядком ее ключей или компаратором, предоставленным во время создания карты, в зависимости от того, какой конструктор используется».

Я бы использовал интерфейс SortedMap вместе с TreeMap. Тогда вы не привязаны к реализации TreeMap.

ScArcher2 21.09.2008 03:45

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

benzado 21.09.2008 03:59

Сортировка ключей требует от компаратора поиска каждого значения для каждого сравнения. Более масштабируемое решение будет использовать 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 может быть повторно использован в качестве возвращаемого значения; для этого потребовалось бы подавить некоторые предупреждения об общих типах, но это могло бы стоить того для повторно используемого библиотечного кода. Кроме того, Comparator не нужно перераспределять при каждом вызове.

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

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

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

Вторая версия может быть более краткой, если вы вернете List <Map.Entry <K, V >>. Это также упрощает итерацию и получение как ключей, так и значений без необходимости выполнять множество дополнительных операций с картой. Это все при условии, что вы согласны с тем, что этот код небезопасен для потоков. Если резервная карта или отсортированный список используются в многопоточной среде, все ставки отключены.

Mike Miller 21.09.2011 01:00

Хорошо, эта версия работает с двумя новыми объектами Map и двумя итерациями и сортировками по значениям. Надеюсь, он работает хорошо, хотя записи карты должны быть зациклены дважды:

public static void main(String[] args) {
    Map<String, String> unsorted = new HashMap<String, String>();
    unsorted.put("Cde", "Cde_Value");
    unsorted.put("Abc", "Abc_Value");
    unsorted.put("Bcd", "Bcd_Value");

    Comparator<String> comparer = new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }};

    System.out.println(sortByValue(unsorted, comparer));

}

public static <K, V> Map<K,V> sortByValue(Map<K, V> in, Comparator<? super V> compare) {
    Map<V, K> swapped = new TreeMap<V, K>(compare);
    for(Entry<K,V> entry: in.entrySet()) {
        if (entry.getValue() != null) {
            swapped.put(entry.getValue(), entry.getKey());
        }
    }
    LinkedHashMap<K, V> result = new LinkedHashMap<K, V>();
    for(Entry<V,K> entry: swapped.entrySet()) {
        if (entry.getValue() != null) {
            result.put(entry.getValue(), entry.getKey());
        }
    }
    return result;
}

Решение использует TreeMap с компаратором и сортирует все нулевые ключи и значения. Во-первых, функция упорядочивания из TreeMap используется для сортировки значений, затем отсортированная карта используется для создания результата, поскольку LinkedHashMap сохраняет тот же порядок значений.

Greetz, GHad

Когда я с этим сталкиваюсь, я просто создаю список на стороне. Если вы объедините их в пользовательскую реализацию Map, это будет приятно ... Вы можете использовать что-то вроде следующего, выполняя сортировку только при необходимости. (Примечание: я на самом деле не тестировал это, но он компилируется ... может быть где-то там небольшая глупая ошибка)

(Если вы хотите, чтобы он был отсортирован как по ключам, так и по значениям, расширьте класс TreeMap, не определяйте методы доступа и пусть мутаторы вызывают super.xxxxx вместо map_.xxxx)

package com.javadude.sample;

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

public class SortedValueHashMap<K, V> implements Map<K, V> {
    private Map<K, V> map_ = new HashMap<K, V>();
    private List<V> valueList_ = new ArrayList<V>();
    private boolean needsSort_ = false;
    private Comparator<V> comparator_;

    public SortedValueHashMap() {
    }
    public SortedValueHashMap(List<V> valueList) {
        valueList_ = valueList;
    }

    public List<V> sortedValues() {
        if (needsSort_) {
            needsSort_ = false;
            Collections.sort(valueList_, comparator_);
        }
        return valueList_;
    }

    // mutators
    public void clear() {
        map_.clear();
        valueList_.clear();
        needsSort_ = false;
    }

    public V put(K key, V value) {
        valueList_.add(value);
        needsSort_ = true;
        return map_.put(key, value);
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        map_.putAll(m);
        valueList_.addAll(m.values());
        needsSort_ = true;
    }

    public V remove(Object key) {
        V value = map_.remove(key);
        valueList_.remove(value);
        return value;
    }

    // accessors
    public boolean containsKey(Object key)           { return map_.containsKey(key); }
    public boolean containsValue(Object value)       { return map_.containsValue(value); }
    public Set<java.util.Map.Entry<K, V>> entrySet() { return map_.entrySet(); }
    public boolean equals(Object o)                  { return map_.equals(o); }
    public V get(Object key)                         { return map_.get(key); }
    public int hashCode()                            { return map_.hashCode(); }
    public boolean isEmpty()                         { return map_.isEmpty(); }
    public Set<K> keySet()                           { return map_.keySet(); }
    public int size()                                { return map_.size(); }
    public Collection<V> values()                    { return map_.values(); }
}

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

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, из которого вы можете получить ключи и значения.

Этот метод намного проще и интуитивно понятнее, чем создание объекта Map <V, List <K>> с почти таким же эффектом. Значения на самом деле не должны быть ключами в объекте Map, в этой ситуации вы действительно ищете список, IMHO.

Jeff Wu 30.12.2011 05:49

Это решение не работает с большим количеством значений, оно испортило мои подсчеты (значение, связанное с каждым ключом)

Sam Levin 15.09.2012 20:17

Это странно. Не могли бы вы уточнить? Каков был ваш результат и какой результат вы ожидали?

Lyudmil 29.09.2012 14:24

Основанный на коде @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
}

Опять же, возможно, лучшим решением было бы просто использовать карту самосортировки, в случае использования org.apache.commons.collections.bidimap.TreeBidiMap

Maxim Veksler 14.04.2009 17:48

Важная заметка:

Этот код может сломаться по-разному. Если вы собираетесь использовать предоставленный код, обязательно прочтите также комментарии, чтобы знать о последствиях. Например, значения больше нельзя получить по их ключу. (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}

Больше нет (stackoverflow.com/questions/109383/…). Кроме того, почему был актерский состав на Double? Разве это не должно быть просто return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b))‌​)?

Stephen 12.08.2010 01:50

@Stephen: Нет. В этом случае все ключи, равные по значению, отбрасываются (разница между равными и сравнением по ссылке). Дополнительно: даже этот код имеет проблемы со следующей последовательностью map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put‌​("D",99.5d);

steffen 20.08.2010 11:00

Компаратор, используемый для древовидной карты, несовместим с equals (см. Javadox sortMap). Это означает, что удаление элементов из древовидной карты не сработает. sorted_map.get ("A") вернет null. Это означает, что использование древовидной карты нарушено.

mR_fr0g 01.12.2010 17:36

@ mR_fr0g Это верно, только если базовая карта изменена. Пока он не изменен, естественный порядок ключей (в соответствии с компаратором) будет соответствовать естественному порядку значений.

aalku 05.11.2011 20:16

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

Maxy-B 24.11.2011 08:37

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

lisak 23.05.2012 18:37

Луи Вассерман (да, один из парней из Google Guava) на самом деле очень не любит этот ответ: «Он ломается несколькими действительно запутанными способами, если вы даже посмотрите на него смешно. Если фоновая карта изменится, она сломается. Если несколько клавиш сопоставить с тем же значением, он сломается. Если вы вызовете get для ключа, которого нет в резервной карте, он сломается. Если вы сделаете что-либо, что приведет к поиску по ключу, которого нет в карта - вызов Map.equals, containsKey, что угодно - она ​​сломается с действительно странными трассировками стека. " plus.google.com/102216152814616302326/posts/bEQLDK712MJ

haylem 04.07.2012 01:19

Если ValueComparator # compare () не возвращает 0 для равенства (см. Отредактированный ответ), комментарий @ Maxy-B об отсутствующих ключах больше не применяется

Tomasz 06.09.2012 21:14

Я сожалею об использовании такого подхода. Теперь я воспроизвел в своем коде все ошибки, описанные здесь, в комментариях. Моя ошибка заключалась в том, что я не читал комментарии. Этот stackoverflow.com/a/2581754/1537800 работает нормально.

Volodymyr Krupach 08.04.2013 19:58

var sorted_map = map.OrderByDescending(entry => entry.Value); (на C#). Простите, не удержался;) +1 хотя

Konrad Morawski 12.09.2013 02:05

Думаю, добавление if (a.equals (b)) return 0; в сравнении сделает его идеальным. Поскольку это устранит повторяющиеся ключи, + сделает возможным нормальный обход. Сообщите мне, если я чего-то упускаю, я собираюсь дать этому шанс в производстве ...: P

Mohammad 11.03.2014 13:05

Разве мы не можем вместо этого просто использовать TreeMap? Я что-нибудь пропустил?

Aniket Kapse 13.04.2015 15:54

@AniketKapse, вы можете использовать только древовидную карту, созданную после заполнения карты (ValueComparator не нужен). Затем вы можете перемещаться по карте, следуя порядку.

Victor 22.08.2015 18:57

@Override public int compare (символ o1, символ o2) {if (charToTimes.get (o1) == null || charToTimes.get (o2) == null) {return -o1.compareTo (o2); } int result = -charToTimes.get (o1) .compareTo (charToTimes.get (o2)); если (результат! = 0) {вернуть результат; } return -o1.compareTo (o2);}

Bruce Zu 14.01.2016 22:28

Разве вы не сравниваете объекты, а не примитивные значения ... поэтому вы не сравниваете позицию в памяти вместо реальных значений?

hephestos 09.10.2018 11:44

возврат -1 для равных значений - очень неправильный способ. Он пройдет небольшие тесты и некоторое время проработает, пока внезапно не выдаст IllegalArgumentException.

Alex Salauyou 16.05.2019 15:20

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

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

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

Обратите внимание, что карта отсортирована от наивысшего значения к наименьшему.

ПРОБЛЕМА: если вы хотите использовать возвращенную карту позже, например, чтобы проверить, содержит ли она определенный элемент, вы всегда получите false из-за вашего настраиваемого компаратора! Возможное решение: замените последнюю строку на: return new LinkedHashMap <K, V> (sortedByValues);

Erel Segal-Halevi 02.10.2011 19:58

Мне это кажется чистым решением, за исключением того факта, что @ErelSegalHalevi указал, что проверка того, существуют ли значения на карте, будет невозможна, поскольку вы указали компаратор. map.put («1», «Один»); map.put («2», «Два»); map.put («3», «Три»); map.put («4», «Четыре»); map.put («5», «Пять»); map.containsKey ("1") всегда будет возвращать false, если вы возвращаете новый объект в функции sortByValues ​​(), например return new TreeMap <K, V> (sortedByValues); решает проблему. Спасибо, Абхи

abhi 14.05.2013 12:14

почти то же самое, что и ответ user157196 и Картера Пейджа. Картер Пейдж содержит исправление LinkedHashMap

Kirby 22.04.2014 21:37

4-я строка решения должна быть int compare = map.get (k1) .compareTo (map.get (k2)); если вам нужен возрастающий порядок

Leo 22.07.2014 12:20

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

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

зачем использовать Collections.sort, если есть Arrays.sort? см. мой ответ для более подробной информации

ciamej 30.05.2012 04:50

Это сэкономило мне кучу времени! Тестовый пример тоже отличный. :) Есть ли причина, по которой вы использовали LinkedHashMap вместо обычного HashMap?

John Mark 19.06.2012 20:06

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

Carter Page 01.07.2012 16:46

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

buzz3791 25.04.2013 19:02

@ buzz3791 Верно. Так будет с любым алгоритмом сортировки. Изменение значения узлов в структуре во время сортировки приводит к непредсказуемым (и почти всегда плохим) результатам.

Carter Page 25.04.2013 22:55

НЕ РАБОТАЕТ, я пробовал ваш код в android, он сортирует хэш-карту по значению, но после этого, когда он выполняет итерацию, чтобы заполнить карту результатов, он снова сортируется по ключу

fire in the hole 01.11.2014 10:53

@Sheagorath Я пробовал на Android, тоже работает. Это не проблема конкретной платформы, учитывая, что вы используете версию Java 6. Правильно ли вы реализовали Сопоставимый в своем объекте значения?

saiyancoder 08.12.2014 04:12

Это решение не сработало для меня. Я использую BigDecimal в качестве объекта значения и Pair <K, V> в качестве ключа (из Apache Commons). При обращении к карте после сортировки значения не сортируются.

anton1980 03.02.2015 01:29

Разве версия Java 8 не должна использовать forEachOrdered вместо forEach, поскольку в документации forEach указано: «Поведение этой операции явно недетерминировано»?

rob 27.06.2015 17:06

полностью разорвал это, но указал @CarterPage в комментариях (в любом случае он будет в проекте с открытым исходным кодом). Спасибо.

Nathan Beach 02.09.2015 06:07

Есть ли конкретная причина для использования LinkedList и LinkedHashMap?

akhil_mittal 06.09.2015 14:55

@akhil_mittal LinkedList следует заменить на ArrayList, но LinkedHashMap необходим, поскольку он сохраняет порядок вставки.

Reinstate Monica 30.09.2015 17:53

@trillions Не могли бы вы рассказать мне, какие изменения вы внесли, чтобы отменить порядок? Спасибо.

cirko 13.11.2015 15:40

+1, но тест junit не самый лучший, по случайности он может провалиться или пройти без изменений в коде

Ferrybig 17.11.2015 14:26

Версия Java 8 должна использовать подход коллекционеров ... Посмотрите на Collectors.toMap.

Alexis C. 21.12.2015 00:25

Версия Java 8 компилируется только в моей среде Eclipse, если я заменяю LinkedHashMap::new на LinkedHashMap<K, V>::new.

basZero 04.10.2016 18:33

Я думаю, что Linked List следует заменить на ArrayList, поскольку мы просто хотим перебирать список.

emilly 28.11.2016 17:50

Возможно, вы захотите заменить for(int i = 0 ; i < 1000 ; ++i) { на while (testMap.size() < 1000) { в модульном тесте, потому что тот же ключ может быть сгенерирован случайным образом. Это может не соответствовать утверждению testMap.size().

R. Oosterholt 05.05.2017 11:20

Не люблю тест, который каждый раз использует разные данные, new Random(System.currentTimeMillis())

weston 15.05.2017 19:35

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

Lii 11.03.2018 01:14

Что, если я хочу отсортировать по ключу (который содержит строку)?

Cecilia 27.10.2019 23:51

@Cecilia, тогда вы заменяете comparingByValue на comparingByKey.

Holger 25.11.2019 13:46

Это потрясающе! Спасибо!

Akanksha 12.03.2021 15:01

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

В примере ниже вы должны добавить TreeMap компаратор в место, где находится *. Но с помощью java API он дает компаратору только ключи, а не значения. Все приведенные здесь примеры основаны на 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()
        );
}

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

Я бы использовал Коллекции 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 невероятно ленивы (что хорошо: вы можете выполнять практически все операции в одно мгновение; настоящая работа выполняется, когда вы начинаете использовать результат), и для этого требуется скопировать карту весь!

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

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

Примечание: это значительно изменилось в июне 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));

Привет, @Stephen, не могли бы вы привести пример использования упорядочивания? Я смотрю исходный код Ordering и совершенно не могу понять, что возвращает .natural (). OnResultOf (...)! Исходный код - "public <F> Ordering <F> onResultOf", я даже не знаю, как он компилируется! Самое главное, как использовать «<F> Упорядочивание <F>» для сортировки карты? Это компаратор что ли? Спасибо.

smallufo 10.11.2010 13:58

Ordering - это просто богатый Comparator. Я пробовал прокомментировать каждый пример (курсив под каждым). «натуральный» означает, что объекты соответствуют стандарту Comparable; это похоже на ComparableComparator apache common. onResultOf применяет функцию к сравниваемому элементу. Итак, если у вас была функция, которая добавляла 1 к целому числу, тогда natural().onResultOf(add1Function).compare(1,2) в конечном итоге выполнял бы 2.compareTo(3)

Stephen 11.11.2010 14:44

ImmutableSortedMap.copyOf выбрасывает исключение IllegalArgumentException, если в исходной карте есть повторяющиеся значения.

lbalazscs 30.04.2013 13:39

@Ibalazscs Да, будет - у вас должна быть возможность использовать ImmutableSetMultiMap или ImmutableListMultiMap для хранения коллекции повторяющихся переменных.

Stephen 01.05.2013 04:31

Спасибо за это, я использовал ваше решение в одном проекте. Я думаю, что здесь есть проблема: чтобы вести себя как карта, она должна возвращать значение, ранее связанное с ключом, если оно существует, но этого никогда не будет. Решение, которое я использовал, - вернуть удаленное значение, если оно существует.

alex 21.08.2013 13:22

В настоящее время class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V>. Почему K extends Comparable<K>? А как насчет только K? Требование, чтобы ключ типа K был Comparable, кажется чрезмерно ограничительным.

Abdull 22.04.2014 13:41

@Abdull, вы, вероятно, захотите прочитать абзац под кодом несколько раз. (Я только что перечитал, и через год у меня возникли небольшие проблемы;)). Мы должны убедиться, что ключи не засоряются (иначе мы могли бы удалить элементы с такими же значениями). Самый простой способ сделать это - сделать ключ сопоставимым. Так что да, это ограничительно, но в противном случае вам понадобится 2 компаратора (что, безусловно, возможно, но выходит за рамки вопроса).

Stephen 22.04.2014 22:07

Разве это не могло бы просто работать без valueMap, используя super.put () в первую очередь? Просто подумал, еще не пробовал ...

Christian Hujer 06.03.2015 01:47

Значение объектаComparator = Ordering.natural (). OnResultOf (Functions.forMap (карта)). Compoun‌ d (Ordering.natural () ‌); приводит к ошибке, не удается разрешить .compound (Ordering <Comparable>)

Ryan The Leach 10.06.2015 05:39

Спасибо за решение, мне пришлось добавить: @Override public boolean containsKey(Object key) { return valueMap.containsKey(key); } для преодоления исключения, возникающего при вызове containsKey.

has981 05.06.2016 10:56

Вот решение 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 );
  }
}

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

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

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

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

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

Это неверно, мой метод работает, если есть повторяющиеся значения. Я использовал его с картами, имеющими более 100 ключей со значением «1».

Anthony 25.02.2012 20:40

public class SortedMapExample {

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

        map.put("Cde", "C");
        map.put("Abc", "A");
        map.put("Cbc", "Z");
        map.put("Dbc", "D");
        map.put("Bcd", "B");
        map.put("sfd", "Bqw");
        map.put("DDD", "Bas");
        map.put("BGG", "Basd");

        System.out.println(sort(map, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                    return o1.compareTo(o2);
            }}));
    }

    @SuppressWarnings("unchecked")
    public static <K, V> Map<K,V> sort(Map<K, V> in, Comparator<? super V> compare) {
        Map<K, V> result = new LinkedHashMap<K, V>();
        V[] array = (V[])in.values().toArray();
        for(int i=0;i<array.length;i++)
        {

        }
        Arrays.sort(array, compare);
        for (V item : array) {
            K key= (K) getKey(in, item);
            result.put(key, item);
        }
        return result;
    }

    public static <K, V>  Object getKey(Map<K, V> in,V value)
    {
       Set<K> key= in.keySet();
       Iterator<K> keyIterator=key.iterator();
       while (keyIterator.hasNext()) {
           K valueObject = (K) keyIterator.next();
           if (in.get(valueObject).equals(value))
           {
                   return valueObject;
           }
       }
       return null;
   }

}

// Пожалуйста, попробуйте здесь. Я изменяю код для сортировки значений.

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

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

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

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

Ответ, за который проголосовали большинство, не работает, когда у вас есть 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.

в примере:

class ValueComparator implements Comparator {

Map base; public ValueComparator(Map base) { this.base = base; }

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

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

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

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

Вы были правы, сначала в коде, который я дал, была какая-то ошибка! Надеюсь, моя последняя редакция поможет вам.

michel.iamit 23.11.2011 17:12

Этот метод просто служит цели. («неудача» заключается в том, что Значения должен реализовывать интерфейс java.util.Comparable)

  /**

 * Sort a map according to values.

 * @param <K> the key of the map.
 * @param <V> the value to sort according to.
 * @param mapToSort the map to sort.

 * @return a map sorted on the values.

 */ 
public static <K, V extends Comparable< ? super V>> Map<K, V>
sortMapByValues(final Map <K, V> mapToSort)
{
    List<Map.Entry<K, V>> entries =
        new ArrayList<Map.Entry<K, V>>(mapToSort.size());  

    entries.addAll(mapToSort.entrySet());

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

    Map<K, V> sortedMap = new LinkedHashMap<K, V>();      

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

    }      

    return sortedMap;

}

http://javawithswaranga.blogspot.com/2011/06/generic-method-to-sort-hashmap.html

Поскольку 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, но если вы собираетесь сразу перебирать его, это излишне ...

это верно, но ваш компаратор не обрабатывает случай равных значений

Sebastien Lorber 27.12.2011 21:48

Несколько простых изменений, чтобы получить отсортированную карту с парами, имеющими повторяющиеся значения. В методе сравнения (класс 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;
        }
      }
    }

Если у вас есть повторяющиеся ключи и только небольшой набор данных (<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.

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

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

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

// 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

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

Вместо использования 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;
}

Мы просто сортируем карту вот так

            Map<String, String> unsortedMap = new HashMap<String, String>();

    unsortedMap.put("E", "E Val");
    unsortedMap.put("F", "F Val");
    unsortedMap.put("H", "H Val");
    unsortedMap.put("B", "B Val");
    unsortedMap.put("C", "C Val");
    unsortedMap.put("A", "A Val");
    unsortedMap.put("G", "G Val");
    unsortedMap.put("D", "D Val");

    Map<String, String> sortedMap = new TreeMap<String, String>(unsortedMap);

    System.out.println("\nAfter sorting..");
    for (Map.Entry <String, String> mapEntry : sortedMap.entrySet()) {
        System.out.println(mapEntry.getKey() + " \t" + mapEntry.getValue());

это просто создает древовидную карту, древовидные карты сортируются по ключу

NimChimpsky 18.09.2012 15:55

Серьезная проблема. Если вы используете первый ответ (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
    }

Теперь, когда вы добавляете две записи с равными значениями, они будут объединены, вы должны вернуть 0 только в том случае, если вы уверены, что объекты одинаковы (равны)

Masood_mj 23.01.2013 08:54

Создайте индивидуальный компаратор и используйте его при создании нового объекта 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}

В случае, если значения равны, я изменил строку «return 1» для сравнения ключей: «return ((String) o1) .compareTo ((String) o2);»

gjgjgj 17.03.2019 20:57

Вы можете попробовать мультикарты Guava:

TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
        Multimaps.invertFrom(Multimaps.forMap(originalMap), 
        ArrayListMultimap.<Integer, String>create()).asMap());

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

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

Map<String, String> map= new TreeMap<String, String>(unsortMap);

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

Это сортировка на основе ключей, а не значений.

Duncan Jones 15.04.2014 11:29

Чтобы добиться этого с помощью новых функций в 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();
}

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

Это возвращает список записей, а не карту, отсортированную по значению. Другая версия, которая возвращает карту: stackoverflow.com/a/22132422/829571

assylias 03.03.2014 21:26

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

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

Как отсортировать в обратном порядке?

Vlad Holubiev 01.06.2014 23:18

@VladGolubev comparing(Entry::getValue).reversed()

assylias 01.06.2014 23:33

нашел решение - Collections.reverseOrder(comparing(Entry::getValue))

Vlad Holubiev 09.08.2014 12:26

Я думаю, что вижу здесь опечатку - не следует ли называть "toMap" как "Collectors.toMap ()"?

Jake Stokes 02.10.2017 17:31

@JakeStokes Или используйте статический импорт :-)

assylias 03.10.2017 08:59

Лучший способ сортировки по значению ввода в обратном порядке: Entry.comparingByValue(Comparator.reverseOrder())

Gediminas Rimsa 24.11.2018 12:20

Я объединил решения user157196 и Carter Page:

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

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

Хорошо, но как насчет использования parallelStream() в этом случае?

Benj 09.12.2014 21:21

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

Brian Goetz 09.12.2014 21:37

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

Benj 10.12.2014 22:30

Как я могу отсортировать по значениям списки разного размера? Я хочу отсортировать список по убыванию.

Pete 28.04.2015 18:04

После сортировки, как мы будем извлекать карту из Stream <>?

CyberMew 06.10.2015 12:18

разве вам не нужно использовать compareByValue в примере из топ-10?

Leo 20.08.2016 02:34

Как скопировать отсортированные значения карты в список?

CKM 04.07.2017 09:25

Часть для создания первой десятки неверна, вам нужно добавить еще два параметра, как указано здесь: stackoverflow.com/a/19671853/5655767

Steven 21.08.2017 14:19

Collectors.toMap здесь по умолчанию будет использовать обычный HashMap, что приведет к отмене упорядочивания. Обратитесь к верхнему ответу для правильного сбора данных до LinkedHashMap.

Vadzim 21.11.2017 03:28

@Vadzim Смысл в том, чтобы захватить топ-10, а не сохранить порядок.

shmosel 21.11.2017 03:47

@shmosel, порядок имеет смысл даже для топ-10;)

Vadzim 21.11.2017 04:50

@Vadzim Не уверен, что вы пытаетесь сказать.

shmosel 21.11.2017 04:51

@Benj он будет работать с точки зрения извлечения топ-10, но получившаяся карта больше не будет заказываться.

OrangeDog 18.06.2019 17:30

Что здесь происходит в версии потока java8, чтобы попасть в топ-10? Может кто-нибудь мне это объяснить?

Sourabh S Nath 11.01.2020 18:17

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

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

Если нет значения, превышающего размер карты, вы можете использовать массивы, это должен быть самый быстрый подход:

public List<String> getList(Map<String, Integer> myMap) {
    String[] copyArray = new String[myMap.size()];
    for (Entry<String, Integer> entry : myMap.entrySet()) {
        copyArray[entry.getValue()] = entry.getKey();
    }
    return Arrays.asList(copyArray);
}

Этот copyArray[entry.getValue()] очень подвержен ошибкам, так как он выйдет из строя, если карта содержит значения, превышающие размер карты.

Tom 16.12.2015 00:57

    static <K extends Comparable<? super K>, V extends Comparable<? super V>>
    Map sortByValueInDescendingOrder(final Map<K, V> map) {
        Map re = new TreeMap(new Comparator<K>() {
            @Override
            public int compare(K o1, K o2) {
                if (map.get(o1) == null || map.get(o2) == null) {
                    return -o1.compareTo(o2);
                }
                int result = -map.get(o1).compareTo(map.get(o2));
                if (result != 0) {
                    return result;
                }
                return -o1.compareTo(o2);
            }
        });
        re.putAll(map);
        return re;
    }
    @Test(timeout = 3000l, expected = Test.None.class)
    public void testSortByValueInDescendingOrder() {
        char[] arr = "googler".toCharArray();
        Map<Character, Integer> charToTimes = new HashMap();
        for (int i = 0; i < arr.length; i++) {
            Integer times = charToTimes.get(arr[i]);
            charToTimes.put(arr[i], times == null ? 1 : times + 1);
        }
        Map sortedByTimes = sortByValueInDescendingOrder(charToTimes);
        Assert.assertEquals(charToTimes.toString(), "{g=2, e=1, r=1, o=2, l=1}");
        Assert.assertEquals(sortedByTimes.toString(), "{o=2, g=2, r=1, l=1, e=1}");
        Assert.assertEquals(sortedByTimes.containsKey('a'), false);
        Assert.assertEquals(sortedByTimes.get('a'), null);
        Assert.assertEquals(sortedByTimes.get('g'), 2);
        Assert.assertEquals(sortedByTimes.equals(charToTimes), true);
    }

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

Я приготовил общий дружественный пример, который решает этот вариант использования. Эта реализация не соблюдает все контракты интерфейса 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;
    }
}

Если Comparable и Comparator не разрешены, как это сделать?

Ved Prakash 28.05.2019 20:41

Не уверен, что понимаю ваш вариант использования, возможно, вы сможете уточнить. Если объект, который вы хотите использовать в качестве значения, не является Comparable, вам нужно будет преобразовать его в объект, который есть.

David Bleckmann 30.05.2019 07:57

Мое решение - довольно простой подход к использованию в основном заданных API. Мы используем функцию карта для экспорта его содержимого как Набор с помощью метода entrySet (). Теперь у нас есть Набор, содержащий объекты Map.Entry.

Хорошо, Set не передает заказ, но мы можем взять контент и поместить его в ArrayList. Теперь он имеет порядок случайный, но мы все равно отсортируем его.

Поскольку ArrayList - это Коллекция, теперь мы используем метод Collections.sort (), чтобы навести порядок в хаосе. Поскольку наши объекты Map.Entry не реализуют нужный нам вид сравнения, мы предоставляем настраиваемый Компаратор.

public static void main(String[] args) {
    HashMap<String, String> map = new HashMap<>();
    map.put("Z", "E");
    map.put("G", "A");
    map.put("D", "C");
    map.put("E", null);
    map.put("O", "C");
    map.put("L", "D");
    map.put("Q", "B");
    map.put("A", "F");
    map.put(null, "X");
    MapEntryComparator mapEntryComparator = new MapEntryComparator();

    List<Entry<String,String>> entryList = new ArrayList<>(map.entrySet());
    Collections.sort(entryList, mapEntryComparator);

    for (Entry<String, String> entry : entryList) {
        System.out.println(entry.getKey() + " : " + entry.getValue());
    }

}

Вот код Java 8 с AbacusUtil

Map<String, Integer> map = N.asMap("a", 2, "b", 3, "c", 1, "d", 2);
Map<String, Integer> sortedMap = Stream.of(map.entrySet()).sorted(Map.Entry.comparingByValue()).toMap(e -> e.getKey(), e -> e.getValue(),
    LinkedHashMap::new);
N.println(sortedMap);
// output: {c=1, a=2, d=2, b=3}

Декларация : Я разработчик AbacusUtil.

Какая часть ответа использует AbacusUtil? Только помощник toMap()?

charles-allen 19.08.2017 20:13

Лучше всего преобразовать HashMap в TreeMap. Ключи сортировки TreeMap сами по себе. Если вы хотите отсортировать значения, чем быстрое исправление, вы можете переключать значения с помощью ключей, если ваши значения не дублируются.

public class Test {
  public static void main(String[] args) {
    TreeMap<Integer, String> hm=new TreeMap();
    hm.put(3, "arun singh");
    hm.put(5, "vinay singh");
    hm.put(1, "bandagi singh");
    hm.put(6, "vikram singh");
    hm.put(2, "panipat singh");
    hm.put(28, "jakarta singh");

    ArrayList<String> al=new ArrayList(hm.values());
    Collections.sort(al, new myComparator());

    System.out.println("//sort by values \n");
    for(String obj: al){
        for(Map.Entry<Integer, String> map2:hm.entrySet()){
            if (map2.getValue().equals(obj)){
                System.out.println(map2.getKey()+" "+map2.getValue());
            }
        } 
     }
  }
}

class myComparator implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
       String o3=(String) o1;
       String o4 =(String) o2;
       return o3.compareTo(o4);
    }   
}

ВЫХОД =

//sort by values 

3 arun singh
1 bandagi singh
28 jakarta singh
2 panipat singh
6 vikram singh
5 vinay singh

Решение O (N ^ 2). И это приведет к ложным результатам, если будут повторяющиеся значения.

Stephen C 07.10.2018 04:05

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

С появлением 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()

Спасибо за эту прокомментированную версию. Один вопрос: в чем разница между использованием Entry.comparingByValue() (как ответ на вопрос выше stackoverflow.com/a/22132422/1480587) или comparing(Entry<Key,Value>::getValue).thenComparing(Entry::g‌​etKey), который вы использовали? Я так понимаю, вы также сравниваете ключи, если значения идентичны, верно? Я заметил, что при сортировке сохраняется порядок элементов с одним и тем же значением - так нужна ли сортировка по ключам, если ключи уже были отсортированы раньше?

Peter T. 11.01.2019 11:58

Если предпочтительнее иметь структуру данных Map, которая по своей сути сортирует по значениям без необходимости запускать какие-либо методы сортировки или явно передавать служебной программе, тогда могут быть применимы следующие решения:

(1) org.drools.chance.core.util.ValueSortedMap (проект JBoss) поддерживает две карты внутри себя: одну для поиска и одну для поддержки отсортированных значений. Очень похоже на ранее добавленные ответы, но, вероятно, это часть абстракции и инкапсуляции (включая механизм копирования), которая делает его более безопасным для использования извне.

(2) http://techblog.molindo.at/2008/11/java-map-sorted-by-value.html избегает поддержки двух карт и вместо этого полагается / расширяется из LinkedMap Apache Common. (Примечание автора блога: as all the code here is in the public domain):

// required to access LinkEntry.before and LinkEntry.after
package org.apache.commons.collections.map;

// SNIP: imports

/**
* map implementation based on LinkedMap that maintains a sorted list of
* values for iteration
*/
public class ValueSortedHashMap extends LinkedMap {
    private final boolean _asc;

    // don't use super()!
    public ValueSortedHashMap(final boolean asc) {
        super(DEFAULT_CAPACITY);
        _asc = asc;
    }

    // SNIP: some more constructors with initial capacity and the like

    protected void addEntry(final HashEntry entry, final int hashIndex) {
        final LinkEntry link = (LinkEntry) entry;
        insertSorted(link);
        data[hashIndex] = entry;
    }

    protected void updateEntry(final HashEntry entry, final Object newValue) {
        entry.setValue(newValue);
        final LinkEntry link = (LinkEntry) entry;
        link.before.after = link.after;
        link.after.before = link.before;
        link.after = link.before = null;
        insertSorted(link);
    }

    private void insertSorted(final LinkEntry link) {
        LinkEntry cur = header;
        // iterate whole list, could (should?) be replaced with quicksearch
        // start at end to optimize speed for in-order insertions
        while ((cur = cur.before) != header & amp; & amp; !insertAfter(cur, link)) {}
        link.after = cur.after;
        link.before = cur;
        cur.after.before = link;
        cur.after = link;
    }

    protected boolean insertAfter(final LinkEntry cur, final LinkEntry link) {
        if (_asc) {
            return ((Comparable) cur.getValue())
            .compareTo((V) link.getValue()) & lt; = 0;
        } else {
            return ((Comparable) cur.getValue())
            .compareTo((V) link.getValue()) & gt; = 0;
        }
    }

    public boolean isAscending() {
        return _asc;
    }
}

(3) Напишите собственный Map или расширенный от LinkedHashMap, который будет сортировать только во время перечисления (например, values(), keyset(), entryset()) по мере необходимости. Внутренняя реализация / поведение абстрагируется от той, которая использует этот класс, но клиенту этого класса кажется, что значения всегда сортируются при запросе для перечисления. Этот класс надеется, что сортировка будет происходить в основном один раз, если все операции put были завершены до перечисления. Метод сортировки использует некоторые из предыдущих ответов на этот вопрос.

public class SortByValueMap<K, V> implements Map<K, V> {

    private boolean isSortingNeeded = false;

    private final Map<K, V> map = new LinkedHashMap<>();

    @Override
    public V put(K key, V value) {
        isSortingNeeded = true;
        return map.put(key, value);
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> map) {
        isSortingNeeded = true;
        map.putAll(map);
    }

    @Override
    public Set<K> keySet() {
        sort();
        return map.keySet();
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        sort();
        return map.entrySet();
    }

    @Override
    public Collection<V> values() {
        sort();
        return map.values();
    }

    private void sort() {
        if (!isSortingNeeded) {
            return;
        }

        List<Entry<K, V>> list = new ArrayList<>(size());

        for (Iterator<Map.Entry<K, V>> it = map.entrySet().iterator(); it.hasNext();) {
            Map.Entry<K, V> entry = it.next();
            list.add(entry);
            it.remove();
        }

        Collections.sort(list);

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

        isSortingNeeded = false;
    }

    @Override
    public String toString() {
        sort();
        return map.toString();
    }
}

(4) Guava предлагает ImmutableMap.Builder.orderEntriesByValue (компаратор valueComparator), хотя результирующая карта будет неизменной:

Configures this Builder to order entries by value according to the specified comparator.

The sort order is stable, that is, if two entries have values that compare as equivalent, the entry that was inserted first will be first in the built map's iteration order.

Я переписал метод devinmoore, который выполняет сортировку карты по ее значению без использования Iterator:

public static Map<K, V> sortMapByValue(Map<K, V> inputMap) {

    Set<Entry<K, V>> set = inputMap.entrySet();
    List<Entry<K, V>> list = new ArrayList<Entry<K, V>>(set);

    Collections.sort(list, new Comparator<Map.Entry<K, V>>()
    {
        @Override
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return (o1.getValue()).compareTo( o2.getValue() );  //Ascending order
        }
    } );

    Map<K, V> sortedMap = new LinkedHashMap<>();

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

    return sortedMap;
}

Note:, что мы использовали LinkedHashMap в качестве выходной карты, потому что наш список был отсортирован по значению, и теперь мы должны сохранить наш список в выходной карте с порядком вставленных ключей, значений. Так что, если вы используете, например, TreeMap в качестве выходной карты, ваша карта снова будет отсортирована по ключам карты!

Это основной метод:

public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    map.put("3", "three");
    map.put("1", "one");
    map.put("5", "five");
    System.out.println("Input Map:" + map);
    System.out.println("Sorted Map:" + sortMapByValue(map));
}

Наконец, вот результат:

Input Map:{1=one, 3=three, 5=five}
Sorted Map:{5=five, 1=one, 3=three}

Простейший метод грубой силы sortHashMap для HashMap<String, Long>: вы можете просто скопируйте это и используйте вот так:

public class Test  {
    public static void main(String[] args)  {
        HashMap<String, Long> hashMap = new HashMap<>();
        hashMap.put("Cat", (long) 4);
        hashMap.put("Human", (long) 2);
        hashMap.put("Dog", (long) 4);
        hashMap.put("Fish", (long) 0);
        hashMap.put("Tree", (long) 1);
        hashMap.put("Three-legged-human", (long) 3);
        hashMap.put("Monkey", (long) 2);

        System.out.println(hashMap);  //{Human=2, Cat=4, Three-legged-human=3, Monkey=2, Fish=0, Tree=1, Dog=4}
        System.out.println(sortHashMap(hashMap));  //{Cat=4, Dog=4, Three-legged-human=3, Human=2, Monkey=2, Tree=1, Fish=0}
    }

    public LinkedHashMap<String, Long> sortHashMap(HashMap<String, Long> unsortedMap)  {
        LinkedHashMap<String, Long> result = new LinkedHashMap<>();

        //add String keys to an array: the array would get sorted, based on those keys' values
        ArrayList<String> sortedKeys = new ArrayList<>();
        for (String key: unsortedMap.keySet())  {
            sortedKeys.add(key);
        }

        //sort the ArrayList<String> of keys    
        for (int i=0; i<unsortedMap.size(); i++)  {
            for (int j=1; j<sortedKeys.size(); j++)  {
                if (unsortedMap.get(sortedKeys.get(j)) > unsortedMap.get(sortedKeys.get(j-1))) {
                    String temp = sortedKeys.get(j);
                    sortedKeys.set(j, sortedKeys.get(j-1));
                    sortedKeys.set(j-1, temp);
                }
            }
        }

        // construct the result Map
        for (String key: sortedKeys)  {
            result.put(key, unsortedMap.get(key));
        }

        return result;
    }
}
Целочисленный литерал имеет тип long если он дополнен буквой ASCII L или же l , например. 4L. 3.10.1. Целочисленные литералы
Kaplan 17.11.2019 15:56

публикация моей версии ответа

List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
    Collections.sort(list, (obj1, obj2) -> obj2.getValue().compareTo(obj1.getValue()));
    Map<String, Integer> resultMap = new LinkedHashMap<>();
    list.forEach(arg0 -> {
        resultMap.put(arg0.getKey(), arg0.getValue());
    });
    System.out.println(resultMap);

Использование библиотеки Guava:

public static <K,V extends Comparable<V>>SortedMap<K,V> sortByValue(Map<K,V> original){
    var comparator = Ordering.natural()
            .reverse() // highest first
            .nullsLast()
            .onResultOf(Functions.forMap(original, null))
            .compound(Ordering.usingToString());
    return ImmutableSortedMap.copyOf(original, comparator);
}

создает список записей для каждого значения, в котором значения сортируются
требуется Java 8 или выше

Map<Double,List<Entry<String,Double>>> sorted =
map.entrySet().stream().collect( Collectors.groupingBy( Entry::getValue, TreeMap::new,
    Collectors.mapping( Function.identity(), Collectors.toList() ) ) );

используя карту {[A = 99,5], [B = 67,4], [C = 67,4], [D = 67,3]}
получает {67.3=[D=67.3], 67.4=[B=67.4, C=67.4], 99.5=[A=99.5]}


… И как получить доступ к каждой записи одну за другой:

sorted.entrySet().forEach( e -> e.getValue().forEach( l -> System.out.println( l ) ) );

D=67.3B=67.4C=67.4A=99.5

Данная карта

   Map<String, Integer> wordCounts = new HashMap<>();
    wordCounts.put("USA", 100);
    wordCounts.put("jobs", 200);
    wordCounts.put("software", 50);
    wordCounts.put("technology", 70);
    wordCounts.put("opportunity", 200);

Сортировка карты по значению в порядке возрастания

Map<String,Integer>  sortedMap =  wordCounts.entrySet().
                                                stream().
                                                sorted(Map.Entry.comparingByValue()).
        collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    System.out.println(sortedMap);

Сортировка карты по значению в порядке убывания

Map<String,Integer>  sortedMapReverseOrder =  wordCounts.entrySet().
            stream().
            sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).
            collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    System.out.println(sortedMapReverseOrder);

Выход:

{программное обеспечение = 50, технологии = 70, США = 100, вакансии = 200, возможности = 200}

{вакансии = 200, возможности = 200, США = 100, технологии = 70, программное обеспечение = 50}

Это работает, но я не понимаю, как порядок элементов влияет на HashMap?

Ali Tou 07.02.2020 14:53

Если вы имеете в виду чистый HashMap, в игру вступает порядок не. Но в LinkedHashMap порядок вставки сохраняется.

WesternGun 18.10.2020 21:07

Использование связанного списка

//Create a list by HashMap
List<Map.Entry<String, Double>> list = new LinkedList<>(hashMap.entrySet());

//Sorting the list
Collections.sort(list, new Comparator<Map.Entry<String, Double>>() {
    public int compare(Map.Entry<String, Double> o1, Map.Entry<String, Double> o2) {
        return (o1.getValue()).compareTo(o2.getValue());
    }
});

//put data from sorted list to hashmap
HashMap<String, Double> sortedData = new LinkedHashMap<>();
for (Map.Entry<String, Double> data : list) {
    sortedData.put(data.getKey(), data.getValue());
}

System.out.print(sortedData);

Это дает дополнительное преимущество, заключающееся в возможности сортировки по возрастанию или убыванию, используя Java 8.

import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.toMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Utils {
    public static Map<String, Integer> sortMapBasedOnValues(Map<String, Integer> map, boolean descending) {
        int multiplyBy = (descending) ? -1: 1;
        Map<String, Integer> sorted =  map.entrySet().stream()
                .sorted(comparingInt(e -> multiplyBy * e.getValue() ))
                .collect(toMap(
                        Map.Entry::getKey, 
                        Map.Entry::getValue,
                        (a, b) -> { throw new AssertionError();},
                        LinkedHashMap::new
                    ));
        return sorted;
    }
}

Простой способ отсортировать любую карту в Java 8 и выше

Map<String, Object> mapToSort = new HashMap<>();

List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet());

Collections.sort(list, Comparator.comparing(o -> o.getValue().getAttribute()));

HashMap<String, Object> sortedMap = new LinkedHashMap<>();
for (Map.Entry<String, Object> map : list) {
   sortedMap.put(map.getKey(), map.getValue());
}

если вы используете Java 7 и ниже

Map<String, Object> mapToSort = new HashMap<>();

List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet());

Collections.sort(list, new Comparator<Map.Entry<String, Object>>() {
    @Override
    public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
       return o1.getValue().getAttribute().compareTo(o2.getValue().getAttribute());      
    }
});

HashMap<String, Object> sortedMap = new LinkedHashMap<>();
for (Map.Entry<String, Object> map : list) {
   sortedMap.put(map.getKey(), map.getValue());
}

map = your hashmap;

List<Map.Entry<String, Integer>> list = new LinkedList<Map.Entry<String, Integer>>(map.entrySet());
Collections.sort(list, new cm());//IMP

HashMap<String, Integer> sorted = new LinkedHashMap<String, Integer>();
for(Map.Entry<String, Integer> en: list){
    sorted.put(en.getKey(),en.getValue());
}

System.out.println(sorted);//sorted hashmap

создать новый класс

class cm implements Comparator<Map.Entry<String, Integer>>{
    @Override
    public int compare(Map.Entry<String, Integer> a, 
                            Map.Entry<String, Integer> b)
    {
        return (a.getValue()).compareTo(b.getValue());
    }
}

Я могу привести вам пример, но уверен, что это то, что вам нужно.

map = {10 = 3, 11 = 1,12 = 2} 

Предположим, вам нужны 2 самых часто используемых ключа: (10, 12) Итак, самый простой способ - использовать PriorityQueue для сортировки по значению карты.

PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> (map.get(a) - map.get(b));
for(int key: map.keySets()) {
   pq.add(key);
   if (pq.size() > 2) {
      pq.poll();
   }
}
// Now pq has the top 2 most frequent key based on value. It sorts the value. 

Этого можно было очень легко достичь с помощью java 8

public static LinkedHashMap<Integer, String> sortByValue(HashMap<Integer, String> map) {

        List<Map.Entry<Integer, String>> list = new ArrayList<>(map.entrySet());
        list.sort(Map.Entry.comparingByValue());
        LinkedHashMap<Integer, String> sortedMap = new LinkedHashMap<>();
        list.forEach(e -> sortedMap.put(e.getKey(), e.getValue()));
        return sortedMap;
    }

Другие вопросы по теме