1890 Stimmen

Sortieren einer Map<Schlüssel, Wert> nach Werten

Ich bin relativ neu in Java und stelle oft fest, dass ich eine Map<Key, Value> auf die Werte.

Da die Werte nicht eindeutig sind, muss ich die keySet in eine array und die Sortierung dieses Arrays durch Array-Sortierung mit einer benutzerdefinierter Komparator die nach dem mit dem Schlüssel verbundenen Wert sortiert.

Gibt es einen einfacheren Weg?

91voto

assylias Punkte 308529

Mit Java 8 können Sie die Streams-Api um dies auf eine wesentlich weniger ausführliche Weise zu tun:

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

32voto

volley Punkte 6555

Beim Sortieren der Schlüssel muss der Comparator jeden Wert für jeden Vergleich nachschlagen. Eine skalierbarere Lösung würde das entrySet direkt verwenden, da dann der Wert für jeden Vergleich sofort verfügbar wäre (obwohl ich dies nicht mit Zahlen untermauert habe).

Hier ist eine generische Version einer solchen Sache:

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

Es gibt Möglichkeiten, die Speicherrotation für die obige Lösung zu verringern. Die erste erstellte ArrayList könnte zum Beispiel als Rückgabewert wiederverwendet werden; dies würde die Unterdrückung einiger generischer Warnungen erfordern, aber es könnte sich für wiederverwendbaren Bibliothekscode lohnen. Außerdem muss der Comparator nicht bei jedem Aufruf neu zugewiesen werden.

Hier ist eine effizientere, wenn auch weniger ansprechende Version:

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

Wenn Sie schließlich ständig auf die sortierten Informationen zugreifen müssen (anstatt sie nur ab und zu zu sortieren), können Sie eine zusätzliche Multikarte verwenden. Lassen Sie es mich wissen, wenn Sie mehr Details benötigen...

25voto

Anthony Punkte 1119

Ich habe mir die gegebenen Antworten angesehen, aber viele von ihnen sind komplizierter als nötig oder entfernen Map-Elemente, wenn mehrere Schlüssel denselben Wert haben.

Hier ist eine Lösung, die meiner Meinung nach besser passt:

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

Beachten Sie, dass die Karte vom höchsten zum niedrigsten Wert sortiert ist.

25voto

p3t0r Punkte 1970

Die commons-collections-Bibliothek enthält eine Lösung namens TreeBidiMap . Sie können auch einen Blick auf die Google Collections API werfen. Sie hat TreeMultimap die Sie verwenden können.

Und wenn Sie diese Frameworks nicht verwenden möchten, werden sie mit Quellcode geliefert.

23voto

Arpan Saini Punkte 3235

Gegebene Karte

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

Sortieren der Karte anhand des Wertes in aufsteigender Reihenfolge

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

Sortieren der Karte nach dem Wert in absteigender Reihenfolge

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

出力します。

{Software=50, Technologie=70, USA=100, Arbeitsplätze=200, Chancen=200}

{Jobs=200, Gelegenheit=200, USA=100, Technologie=70, Software=50}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X