Сортировать карту по значениям

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

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

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

20.09.2008 21:03:23
Карта не предназначена для сортировки, но к ней можно быстро получить доступ. Объект равных значений нарушает ограничение карты. Используйте набор записей, как List<Map.Entry<...>> list =new LinkedList(map.entrySet())и Collections.sort ....так.
Hannes 9.02.2014 17:34:42
Случай, когда это может возникнуть, когда мы пытаемся использовать счетчик в Java (Map <Object, Integer>). Сортировка по количеству вхождений будет обычной операцией. Такой язык, как Python, имеет встроенную структуру данных Counter. Для альтернативного способа реализации в Java, вот пример
demongolem 21.12.2017 20:03:58
Существует много вариантов использования для отсортированных карт, поэтому у вас есть TreeMap и ConcurrentSkipListMap в jdk.
alobodzk 22.04.2018 19:10:45
Raedwald 23.07.2019 10:37:05
30 ОТВЕТОВ

С 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;
}
185
14.06.2016 07:51:07
Список для сортировки "новый LinkedList" ?? Gee. К счастью, Collections.sort () сначала выводит список в массив, чтобы избежать именно такого рода ошибок (но, тем не менее, выгрузка ArrayList в массив должна быть быстрее, чем делать то же самое для LinkedList).
Dimitris Andreou 8.04.2010 13:13:35
не может конвертировать из Iterator в TernaryTree.Iterator
lisak 3.06.2011 16:31: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 19:29:47
с использованием вышеуказанных значений отсортировано в порядке возрастания. Как отсортировать по убыванию?
ram 16.04.2015 13:09:58
Замените o1 и o2 для сортировки по убыванию.
Soheil 5.06.2017 20:14:11

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

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

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

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

26
30.05.2011 18:59:51
Вам не нужно использовать коллекцию общин. Java поставляется со своим собственным java.util.TreeMap.
yoliho 21.09.2008 04:23:04
да, но TreeMap гораздо менее гибок при сортировке по значению части картографических записей.
p3t0r 21.09.2008 06:18:20
Проблема с BidiMap заключается в том, что он добавляет ограничение отношения 1: 1 между ключами и значениями, чтобы сделать отношение обратимым (т. Е. И ключи, и значения должны быть уникальными). Это означает, что вы не можете использовать это для хранения чего-то вроде объекта подсчета слов, так как многие слова будут иметь одинаковое количество.
Doug 23.07.2010 19:49:15

Если ваши значения Map реализуют 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);
-9
20.09.2008 21:16:42
Согласовано. Просто и имеет смысл по сравнению с другими материалами здесь. Я не уверен, почему все остальные предлагают более сложные способы решить эту проблему, когда Коллекции уже сделали это для вас.
Aaron 21.09.2008 19:27:08
Причина в том, что это не решает проблему. Он правильно сортирует значения, но отбрасывает ключи. Вопрос, который задавался, был способом сортировки карты, означая, что ключи и значения все еще должны быть связаны.
gregory 20.10.2008 13:22:17
Не сработает, потому что вы просто сортируете копию значений, оставляя карту нетронутой.
whiskeysierra 10.01.2010 14:05:23

Для сортировки по ключам я нашел лучшее решение с 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}

-2
30.05.2011 19:44:02
поместите все значения в treeMap автоматически, они будут отсортированы по его ключу, используя RB Tree.
sitakant 15.12.2015 11:19:40
Компаратор, переданный конструктору TreeMap, сравнивает КЛЮЧИ TreeMap вместо ЗНАЧЕНИЙ. В вашем примере это сработало, потому что сортировка по ключам такая же, как сортировка по значениям. Полный конструктор: public TreeMap (Comparator <? Super K> компаратор), где он принимает потомки K, который является ключом. Для получения дополнительной информации обращайтесь по адресу
Hussein El Motayam 6.07.2017 11:41:17

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

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

-11
20.09.2008 22:47:31
Я бы использовал интерфейс SortedMap вместе с TreeMap. Тогда вы не привязаны к реализации TreeMap.
ScArcher2 20.09.2008 23:45:22
В документации говорится, что TreeMap сортирует свои ключи в соответствии с их естественным порядком или предоставленным компаратором. Но сортировка основана на ключах, а не на значениях. Компаратор, который сравнивал значения, дал бы древовидную структуру, аналогично использованию значения в качестве ключа.
benzado 20.09.2008 23:59:05

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

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

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

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

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

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

31
1.10.2008 21:02:28
Вторая версия может быть более краткой, если вы вернете List <Map.Entry <K, V >>. Это также упрощает итерацию и получение как ключей, так и значений, не прибегая к большому количеству дополнительных переходов на карту. Это все, если вы согласны с тем, что этот код небезопасен. Если карта поддержки или отсортированный список являются общими в многопоточной среде, все ставки отключены.
Mike Miller 20.09.2011 21:00:55

Хорошо, эта версия работает с двумя новыми объектами 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 с Comparator и сортирует все нулевые ключи и значения. Сначала для сортировки значений используется функция упорядочения из TreeMap, затем отсортированная карта используется для создания результата в виде LinkedHashMap, который сохраняет тот же порядок значений.

Привет, GHad

-2
21.09.2008 01:37:01

Когда я сталкиваюсь с этим, я просто создаю список на стороне. Если вы соберете их вместе в пользовательскую реализацию 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(); }
}
2
21.09.2008 15:03:21

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

public class MapUtilities {

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

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

}

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

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

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

}

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

14
24.09.2008 00:58:27
Этот метод намного проще и более интуитивно понятен, чем создание объекта Map <V, List <K >> с почти таким же эффектом. Значения не должны быть ключами в объекте Map, в действительности вам нужен список в этой ситуации, IMHO.
Jeff Wu 30.12.2011 01:49:48
Это решение не работает с большим количеством значений, оно связано с моими подсчетами (значениями, связанными с каждым ключом)
Sam Levin 15.09.2012 16:17:33
Это странно. Не могли бы вы уточнить? Каким был ваш выход и какой выход вы ожидали?
Lyudmil 29.09.2012 10:24:53

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

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

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

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

    return sortMap(map, comparator);
}

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

    return sortMap(map, comparator);
}

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

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

    return 0;
}

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

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

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

    return result;
}

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

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

Этот код может сломаться несколькими способами. Если вы намереваетесь использовать предоставленный код, обязательно прочитайте комментарии, чтобы знать о последствиях. Например, значения больше не могут быть получены по их ключу. ( 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}
419
18.04.2016 23:41:20
Больше нет ( stackoverflow.com/questions/109383/… ). Кроме того, почему был приведен дубль? Разве это не должно быть просто return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))?
Stephen 11.08.2010 21:50:54
@Stephen: Нет. В этом случае все равные по значению ключи отбрасываются (разница между равными и сравнением по ссылке). Дополнительно: даже у этого кода есть проблемы со следующей последовательностью map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
steffen 20.08.2010 07:00:23
Компаратор, используемый для древовидной карты, несовместим с equals (см. Javadox sortMap). Это означает, что удаление элементов с древовидной карты не будет работать. sorted_map.get ("A") вернет ноль. Это означает, что это использование treemap не работает.
mR_fr0g 1.12.2010 14:36:50
На всякий случай, если людям не ясно: это решение, вероятно, не будет делать то, что вы хотите, если у вас есть несколько ключей, сопоставляемых с одним и тем же значением - только один из этих ключей появится в отсортированном результате.
Maxy-B 24.11.2011 04:37:55
Луи Вассерман (да, один из ребят из Google Guava) на самом деле очень не любит этот ответ: «Он ломается в нескольких очень запутанных направлениях, если вы даже посмотрите на это смешно. Если карта поддержки меняется, она сломается. Если несколько ключей сопоставить с тем же значением, оно сломается. Если вы вызовете get для ключа, которого нет в резервной карте, оно сломается. Если вы сделаете что-либо, что вызовет поиск ключа, которого нет в карта - вызов Map.equals, содержит ключ, что угодно - она ​​порвет с очень странными следами стека. " plus.google.com/102216152814616302326/posts/bEQLDK712MJ
haylem 3.07.2012 21:19:30

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

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

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

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

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

Вот универсальная версия:

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;
    }
}
893
3.04.2018 00:00:27
Рад, что это помогает. Джон, LinkedHashMap важен для решения, так как он обеспечивает предсказуемый порядок итераций.
Carter Page 1.07.2012 12:46:16
@ buzz3791 Правда. Это будет иметь место в любом алгоритме сортировки. Изменение значения узлов в структуре во время сортировки приводит к непредсказуемым (и почти всегда плохим) результатам.
Carter Page 25.04.2013 18:55:17
@Sheagorath Я попробовал это в Android, и это тоже работает. Это не проблема конкретной платформы, учитывая, что вы используете версию Java 6. Правильно ли вы реализовали Comparable в своем объекте значения?
saiyancoder 8.12.2014 01:12:29
Разве не следует использовать версию Java 8 forEachOrderedвместо forEachдокументов forEachсостояния: «Поведение этой операции явно недетерминировано»?
rob 27.06.2015 14:06:30
полностью разорвал это, но зачислил @CarterPage в комментариях (это будет в любом случае в проекте с открытым исходным кодом). Огромное спасибо.
Nathan Beach 2.09.2015 03:07:48

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

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

Пример:

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

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

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

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

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

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

    public Float getTime() {
        return time;
    }

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

    public Driver getDriver() {
        return driver;
    }

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

и класс Comparator:

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

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

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

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

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

Я бы использовал Google Collections Guava для этого - если ваши значения, 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. Не пытайтесь сравнивать выше на a TreeMap; нет смысла пытаться сравнивать вставленный ключ, когда он не имеет значения, до окончания операции, т. е. он очень быстро сломается

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

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

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

Примечание. Это значительно изменилось в июне 2012 года - предыдущий код никогда не работал: требуется внутренний HashMap для поиска значений без создания бесконечного цикла между TreeMap.get()-> compare()и compare()->get()

import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import com.google.common.base.Functions;
import com.google.common.collect.Ordering;

class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
    //A map for doing lookups on the keys for comparison so we don't get infinite loops
    private final Map<K, V> valueMap;

    ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
        this(partialValueOrdering, new HashMap<K,V>());
    }

    private ValueComparableMap(Ordering<? super V> partialValueOrdering,
            HashMap<K, V> valueMap) {
        super(partialValueOrdering //Apply the value ordering
                .onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
                .compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
        this.valueMap = valueMap;
    }

    public V put(K k, V v) {
        if (valueMap.containsKey(k)){
            //remove the key in the sorted set before adding the key again
            remove(k);
        }
        valueMap.put(k,v); //To get "real" unsorted values for the comparator
        return super.put(k, v); //Put it in value order
    }

    public static void main(String[] args){
        TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
        map.put("a", 5);
        map.put("b", 1);
        map.put("c", 3);
        assertEquals("b",map.firstKey());
        assertEquals("a",map.lastKey());
        map.put("d",0);
        assertEquals("d",map.firstKey());
        //ensure it's still a map (by overwriting a key, but with a new value) 
        map.put("d", 2);
        assertEquals("b", map.firstKey());
        //Ensure multiple values do not clobber keys
        map.put("e", 2);
        assertEquals(5, map.size());
        assertEquals(2, (int) map.get("e"));
        assertEquals(2, (int) map.get("d"));
    }
 }

Когда мы помещаем, мы гарантируем, что хэш-карта имеет значение для компаратора, а затем помещаем его в TreeSet для сортировки. Но перед этим мы проверяем хеш-карту, чтобы увидеть, что ключ на самом деле не является дубликатом. Кроме того, созданный нами компаратор также будет включать ключ, чтобы дублированные значения не удаляли неповторяющиеся ключи (из-за сравнения ==). Эти 2 пункта жизненно важны для гарантии сохранения контракта на карту; если вы думаете, что не хотите этого, то вы почти полностью перевернули карту (в Map<V,K>).

Конструктор должен быть вызван как

 new ValueComparableMap(Ordering.natural());
 //or
 new ValueComparableMap(Ordering.from(comparator));
211
23.05.2017 12:26:34
Привет @Stephen, можете ли вы привести пример, как использовать порядок? Я изучаю исходный код Ordering и совершенно не могу понять, что .natural (). OnResultOf (...) возвращает! Исходный код "public <F> Ordering <F> onResultOf", я даже не знаю, как он компилируется! Самое главное, как использовать <F> Ordering <F> "для сортировки карты? Это компаратор или что-то? Спасибо.
smallufo 10.11.2010 10:58:52
Orderingэто просто богатый Comparator. Я пытался комментировать каждый пример (курсив под каждым). «естественный» указывает, что объекты Comparable; Это как ComparableComparator Apache Common. onResultOfприменяет функцию к сравниваемому элементу. Итак, если бы у вас была функция, которая добавляла 1 к целому числу, то natural().onResultOf(add1Function).compare(1,2)в конечном итоге это делалось бы2.compareTo(3)
Stephen 11.11.2010 11:44:54
ImmutableSortedMap.copyOf создает исключение IllegalArgumentException, если в исходной карте есть повторяющиеся значения.
lbalazscs 30.04.2013 09:39:17
@Ibalazscs Да, будет - Вы должны иметь возможность использовать ImmutableSetMultiMapили ImmutableListMultiMapсодержать коллекцию дубликатов переменных.
Stephen 1.05.2013 00:31:18
Спасибо за это, я использовал ваше решение в одном проекте. Я думаю, что проблема заключается в том, что для того, чтобы вести себя как карта, нужно возвращать значение, ранее связанное с ключом, если оно существует, но так никогда не получится. Решение, которое я использовал, - вернуть удаленное значение, если оно существует.
alex 21.08.2013 09:22:37

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

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

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

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

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

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

    clear();

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

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

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

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

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

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

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

4
2.01.2011 05:53:22

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

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

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

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

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

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

8
9.02.2011 10:21:25
Это неправильно, мой метод работает, если есть повторяющиеся значения. Я использовал его с картами, имеющими более 100 ключей со значением «1».
Anthony 25.02.2012 16:40:53
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;
   }

}

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

-1
30.05.2011 19:56:47

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

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

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

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

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

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

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

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

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));
    }
}
12
9.09.2014 06:43:09

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

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

ключ / значение: D / 67,3
ключ / значение: A / 99,5
ключ / значение: B / 67,4
ключ / значение: C / 67,5
ключ / значение: E / 99,5

полученные результаты

ключ / значение: A / 99,5
ключ / значение: C / 67,5
ключ / значение: B / 67,4
ключ / значение: D / 67,3

Так что пропускаем E !!

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

в примере:

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

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

public int сравнивать (Объект a, Объект b) {

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

}}

теперь возвращается:

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

ключ / значение: D / 67,3
ключ / значение: A / 99,5
ключ / значение: B / 67,4
ключ / значение: C / 67,5
ключ / значение: E / 99,5

Результаты:

ключ / значение: A / 99,5
ключ / значение: E / 99,5
ключ / значение: C / 67,5
ключ / значение: B / 67,4
ключ / значение: D / 67,3

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

package nl.iamit.util;

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

public class Comparators {


    public static class MapIntegerStringComparator implements Comparator {

        Map<Integer, String> base;

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

        public int compare(Object a, Object b) {

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


}

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

package test.nl.iamit.util;

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

public class TestComparators {


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

        sorted_map.putAll(unSoretedMap);

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

        assertArrayEquals(targetKeys,currecntKeys);
    }
}

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

public static class MapStringDoubleComparator implements Comparator {

    Map<String, Double> base;

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

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

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

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

    sorted_map.putAll(unSoretedMap);

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

    assertArrayEquals(targetKeys,currecntKeys);
}

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

11
23.11.2011 13:11:46
Вы были правы, сначала была ошибка в коде, который я дал! Я надеюсь, что мое недавнее редактирование поможет вам.
michel.iamit 23.11.2011 13:12:40

Этот метод будет просто служить цели. («Задержка» заключается в том, что значения должны реализовывать интерфейс 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

2
23.08.2011 18:52:42

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

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

    return list;
}

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

5
31.08.2011 20:40:25
это верно, но ваш компаратор не обрабатывает случай равных значений
Sebastien Lorber 27.12.2011 17:48:33

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

    public int compare(Object a, Object b) {

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

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

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

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

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

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

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

3
26.09.2011 21:39:32

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

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

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

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

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


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

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




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

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


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

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

Исполнительный директор: http://www.ideone.com/dq3Lu

Выход:

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

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

4
27.12.2011 17:43:44

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

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

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

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

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

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

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

    return result;
}
9
30.05.2012 00:47:27

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

            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());
-3
29.08.2012 07:33:26
это просто создает древовидную карту, древовидные карты сортируются по ключу
NimChimpsky 18.09.2012 11:55:11

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

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

        return 0;
        // returning 0 would merge keys
    }
7
18.11.2012 06:37:50
Теперь, когда вы добавляете две записи с одинаковыми значениями, они будут объединены, вы должны возвращать 0, только если вы уверены, что объекты одинаковы (равны)
Masood_mj 23.01.2013 04:54:00