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

1390

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

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

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

  • 18
    Карта не предназначена для сортировки, но к ней можно быстро получить доступ. Объект равных значений нарушает ограничение карты. Используйте набор List<Map.Entry<...>> list =new LinkedList(map.entrySet()) , например List<Map.Entry<...>> list =new LinkedList(map.entrySet()) и Collections.sort .... таким образом.
  • 0
    Случай, когда это может возникнуть, когда мы пытаемся использовать счетчик в Java (Map <Object, Integer>). Сортировка по количеству вхождений будет обычной операцией. Такой язык, как Python, имеет встроенную структуру данных Counter. Для альтернативного способа реализации в Java, вот пример
Показать ещё 1 комментарий
Теги:
sorting
dictionary
collections

49 ответов

859

Здесь общая версия:

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;
    }
}
  • 0
    зачем использовать Collections.sort, если есть Arrays.sort? см мой ответ для более подробной информации
  • 0
    Это сэкономило мне массу времени! Тестовый пример тоже отличный. :) Есть ли причина, по которой вы использовали LinkedHashMap, а не обычный HashMap?
Показать ещё 19 комментариев
407

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

Этот код может разбиваться несколькими способами. Если вы намерены использовать предоставленный код, обязательно прочитайте комментарии, чтобы быть в курсе последствий. Например, значения больше не могут быть извлечены по их ключу. (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}
  • 16
    Больше нет ( stackoverflow.com/questions/109383/… ). Кроме того, почему был приведен дубль? return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b))) не должно быть просто return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b))) ?
  • 12
    @Stephen: Нет. В этом случае все равные по значению ключи отбрасываются (разница между равными и сравнением по ссылке). Дополнительно: даже у этого кода есть проблемы со следующей последовательностью map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
Показать ещё 13 комментариев
261

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);
  • 0
    Хорошо, но как насчет использования parallelStream() в этом случае?
  • 8
    Он будет работать параллельно, однако вы можете обнаружить, что стоимость объединения карт для объединения частичных результатов слишком высока, и параллельная версия может работать не так хорошо, как вы ожидаете. Но это работает и дает правильный ответ.
Показать ещё 10 комментариев
218

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

Я бы использовал Google Collections Guava, чтобы сделать это - если ваши значения Comparable, то вы можете использовать

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))

Что создаст функцию (объект) для карты [которая берет любую из клавиш в качестве входных данных, возвращает соответствующее значение], а затем применяет к ним естественный (сопоставимый) порядок [значения].

Если они не сопоставимы, вам нужно что-то сделать по линиям

valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map)) 

Они могут применяться к TreeMap (как Ordering extends Comparator) или LinkedHashMap после некоторой сортировки

Примечание. Если вы собираетесь использовать TreeMap, помните, что если сравнение == 0, то элемент уже находится в списке (что произойдет, если у вас есть несколько значений, которые сравнивают их). Чтобы облегчить это, вы можете добавить свой ключ к компаратору, как это (предполагая, что ваши ключи и значения Comparable):

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())

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

Обратите внимание, что это все равно не будет работать, если ваши ключи сравниваются с 0, но этого должно быть достаточно для большинства элементов Comparable (поскольку hashCode, equals и compareTo часто синхронизируются...)

См. Ordering.onResultOf() и Functions.forMap().

Реализация

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

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

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

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

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

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

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

Примечание. Это значительно изменилось в июне 2012 года - предыдущий код никогда не работал: для поиска значений без необходимости создания бесконечного цикла между TreeMap.get()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 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));
  • 0
    Привет @Stephen, можете ли вы привести пример, как использовать порядок? Я смотрю на исходный код Ordering и совершенно не могу понять, что .natural (). OnResultOf (...) возвращает! Исходный код "public <F> Ordering <F> onResultOf", я даже не знаю, как он компилируется! Самое главное, как использовать <F> Ordering <F> "для сортировки карты? Это компаратор или что-то? Благодарю.
  • 0
    Ordering просто богатый Comparator . Я пытался комментировать каждый пример (курсив под каждым). «натуральный» означает, что объекты Comparable ; Это как ComparableComparator Apache Common. onResultOf применяет функцию к сравниваемому элементу. Таким образом, если бы у вас была функция, которая добавила 1 к целому числу, то natural().onResultOf(add1Function).compare(1,2) в конечном итоге выполнило бы 2.compareTo(3)
Показать ещё 8 комментариев
176

От 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;
}
  • 16
    Список, который нужно отсортировать - это «новый LinkedList» ?? Gee. К счастью, Collections.sort () сначала выводит список в массив, чтобы избежать именно такого рода ошибок (но, тем не менее, выгрузка ArrayList в массив должна быть быстрее, чем делать то же самое для LinkedList).
  • 0
    невозможно преобразовать из Iterator в TernaryTree.Iterator
Показать ещё 7 комментариев
43

С помощью 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));
  • 0
    Как отсортировать в обратном порядке?
  • 0
    comparing(Entry::getValue).reversed() @VladGolubev comparing(Entry::getValue).reversed()
Показать ещё 5 комментариев
29

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

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

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

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

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

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

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

private static final Comparator SINGLE = new ValueComparator();

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

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

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

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

  • 0
    Вам не нужно использовать коллекцию общин. Java поставляется со своим собственным java.util.TreeMap.
  • 2
    да, но TreeMap гораздо менее гибок при сортировке по значению части картографических записей.
Показать ещё 1 комментарий
22

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

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

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

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

  • 6
    ПРОБЛЕМА: если вы хотите использовать возвращенную карту позже, например, чтобы проверить, содержит ли она определенный элемент, вы всегда получите false из-за вашего собственного компаратора! Возможное решение: заменить последнюю строку на: return new LinkedHashMap <K, V> (sortedByValues);
  • 0
    Это выглядит чистым решением для меня, за исключением того факта, что @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); решает проблему. Спасибо абхи
Показать ещё 2 комментария
17

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

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

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

Создайте настраиваемый компаратор и используйте его при создании нового объекта 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}
  • 1
    Это работает и для дубликатов значений !!!
  • 0
    В случае, если значения равны, я изменил строку «return 1» для сравнения ключей: «return ((String) o1) .compareTo ((String) o2);»
14

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

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

}

И вот неудобно неполное unit test:

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

  • 0
    Этот метод намного проще и более интуитивно понятен, чем создание объекта Map <V, List <K >> с почти таким же эффектом. Значения не должны быть ключами в объекте Map, в действительности вам нужен список в этой ситуации, ИМХО.
  • 0
    Это решение не работает с большим количеством значений, оно связано с моими значениями (значения, связанные с каждым ключом)
Показать ещё 1 комментарий
11

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

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

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

Результаты

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

Таким образом, исключается E!!

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

в примере:

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

     

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

     

public int compare (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 (2011 ноябрь 22): Я использую это решение для карты Integer Id и имен, но идея одинакована, поэтому может оказаться, что код выше неверен (я напишу его в тесте и дам вам правильный код), это код для сортировки карты на основе вышеприведенного решения:

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

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

  • 1
    У меня не работает. Я получаю все значения как ноль.
  • 0
    Вы были правы, в коде, который я дал сначала, была какая-то ошибка! Я надеюсь, что мое недавнее редактирование поможет вам.
11

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

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

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

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

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

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

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

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

Основная проблема. Если вы используете первый ответ (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
    Теперь, когда вы добавляете две записи с одинаковыми значениями, они будут объединены, вы должны вернуть только 0, если вы уверены, что объекты одинаковы (равны)
6

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

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

import java.util.*;

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

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

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

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

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

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

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

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

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

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

                    public V getValue() {
                        return v;
                    }

                    public V setValue(V v) {
                        throw new UnsupportedOperationException();
                    }
                });
            }
        }
        return ret;
    }
}
6

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

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
5

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

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

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

В примере ниже вы должны добавить 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()
        );
}
5

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

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

Конечно, решение Стивена действительно здорово, но для тех, кто не может использовать 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 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

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

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

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

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

/**
 * Sort a map by it 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 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 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 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
}
  • 0
    С другой стороны, возможно, лучшим решением было бы просто использовать самосортирующую карту, в случае использования org.apache.commons.collections.bidimap.TreeBidiMap
3

Поздняя запись.

С появлением 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()
  • 0
    Спасибо за эту прокомментированную версию. Один вопрос: в чем разница с использованием Entry.comparingByValue() (в ответе ассилий выше на stackoverflow.com/a/22132422/1480587 ) или comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey) что ты использовал? Я так понимаю, вы также сравниваете ключи, если значения идентичны, верно? Я заметил, что сортировка поддерживает порядок элементов с одинаковым значением - так нужна ли сортировка по ключам, если ключи уже были отсортированы ранее?
3

Я объединил решения user157196 и Carter Страница:

class MapUtil {

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

}

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

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

    public int compare(K a, K b) {
        int result = (base.get(a).compareTo(base.get(b)));
        if (result == 0) result=1;
        // returning 0 would merge keys
        return result;
    }
}
3

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

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

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

3

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

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

2

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

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

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

Самый простой метод 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;
    }
}
0

Я переписал метод 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}
0

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

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

(2) http://techblog.molindo.at/2008/11/java-map-sorted-by-value.html избегает сохранения двух карт и вместо этого полагается/выходит из Apache Common LinkedMap. (Примечание автора блога: 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 из 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(Comparator valueComparator), хотя результирующая карта будет неизменной:

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

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

0
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
  • 0
    O (N ^ 2) решение. И это будет давать ложные результаты, если есть повторяющиеся значения.
0

Вот код 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.

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

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

Хорошо, набор не выполняет порядок, но мы можем взять содержимое и поместить его в 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());
    }

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

}

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

  • 0
    Это в О (п ^ 2) ...
-1
    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);
    }
-1

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

            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());
  • 9
    это просто создает древовидную карту, древовидные карты сортировать по ключу
-2

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

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);
}
  • 0
    Этот copyArray[entry.getValue()] очень подвержен ошибкам, так как он потерпит неудачу, если карта содержит значения, которые превышают размер карты.
-2

Хорошо, эта версия работает с двумя новыми объектами 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

  • 1
    Не работает с дублирующимися значениями.
-2

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

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

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

-10

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

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

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

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

Если значения вашей карты реализуют 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);
  • 1
    Согласовано. Просто и имеет смысл по сравнению с другими материалами здесь. Я не уверен, почему все остальные предлагают более сложные способы решить эту проблему, когда Коллекции уже сделали это для вас.
  • 19
    Причина в том, что это не решает проблему. Он правильно сортирует значения, но отбрасывает ключи. Вопрос, который задавался, был способом сортировки карты, означая, что ключи и значения все еще должны быть связаны.
Показать ещё 1 комментарий
-11

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

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

  • 0
    Я бы использовал интерфейс SortedMap вместе с TreeMap. Тогда вы не привязаны к реализации TreeMap.
  • 8
    В документации говорится, что TreeMap сортирует свои ключи в зависимости от их естественного порядка или от предоставленного вами компаратора. Но сортировка основана на ключах, а не на значениях. Компаратор, который сравнивал значения, давал бы древовидную структуру, аналогично использованию значения в качестве ключа.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню