wake-up-neo.com

Sortieren Sie eine Map <Key, Value> nach Werten

Ich bin relativ neu in Java und finde oft, dass ich einen Map<Key, Value> nach den Werten sortieren muss.

Da die Werte nicht eindeutig sind, konvertiere ich die keySet in eine array und sortiere das Array durch array sort mit einem benutzerdefinierten Komparator der nach dem mit dem Schlüssel verknüpften Wert sortiert.

Gibt es einen einfacheren Weg?

1473
Abe

Hier ist eine generischfreundliche Version:

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;
    }
}
824
Carter Page

Wichtige Notiz:

Dieser Code kann auf mehrere Arten beschädigt werden. Wenn Sie den bereitgestellten Code verwenden möchten, lesen Sie auch die Kommentare, um sich der Auswirkungen bewusst zu sein. Zum Beispiel können Werte nicht mehr über ihren Schlüssel abgerufen werden. (get gibt immer null zurück.)


Es scheint viel einfacher als alles Vorstehende. Verwenden Sie eine TreeMap wie folgt:

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

Ausgabe:

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}
408
user157196

Java 8 bietet eine neue Antwort: Konvertieren Sie die Einträge in einen Stream und verwenden Sie die Vergleicherkombinationen von Map.Entry:

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

Auf diese Weise können Sie die Einträge in aufsteigender Reihenfolge nach Wert sortieren. Wenn Sie einen absteigenden Wert wünschen, kehren Sie den Vergleicher einfach um:

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

Wenn die Werte nicht vergleichbar sind, können Sie einen expliziten Vergleicher übergeben:

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

Sie können dann andere Stream-Vorgänge verwenden, um die Daten zu verwenden. Wenn Sie beispielsweise die Top 10 in einer neuen Karte anzeigen möchten:

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

Oder drucken Sie an System.out:

map.entrySet().stream()
   .sorted(Map.Entry.comparingByValue())
   .forEach(System.out::println);
254
Brian Goetz

Drei einzeilige Antworten ...

Ich würde ... benutzen Google-Sammlungen Guava Um dies zu tun - wenn Ihre Werte Comparable sind, können Sie dies verwenden

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

Dadurch wird eine Funktion (ein Objekt) für die Karte erstellt [die einen beliebigen Schlüssel als Eingabe übernimmt und den entsprechenden Wert zurückgibt], und dann eine natürliche (vergleichbare) Reihenfolge auf sie [die Werte] anwenden.

Wenn sie nicht vergleichbar sind, müssen Sie etwas in der Richtung von tun

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

Diese können auf eine TreeMap angewendet werden (wie Ordering erweitert Comparator) oder eine LinkedHashMap nach einiger Sortierung

NB: Wenn Sie eine TreeMap verwenden, denken Sie daran, dass das Element bereits in der Liste enthalten ist, wenn ein Vergleich == 0 ist (was geschieht, wenn Sie mehrere Werte haben, die dasselbe vergleichen). . Um dies zu erleichtern, können Sie Ihren Schlüssel wie folgt zum Vergleicher hinzufügen (vorausgesetzt, Ihre Schlüssel und Werte sind Comparable):

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

= Übernehmen Sie die natürliche Reihenfolge auf den durch den Schlüssel gemachten Wert und setzen Sie diese mit der natürlichen Reihenfolge des Schlüssels zusammen.

Beachten Sie, dass dies immer noch nicht funktioniert, wenn Ihre Schlüssel mit 0 verglichen werden, dies sollte jedoch für die meisten comparable-Elemente ausreichend sein (da hashCode, equals und compareTo häufig synchron sind ...)

Siehe Ordering.onResultOf () und Functions.forMap () .

Implementierung

Jetzt, da wir einen Vergleicher haben, der das tut, was wir wollen, müssen wir ein Ergebnis daraus erzielen. 

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

Nun wird dies höchstwahrscheinlich funktionieren, aber:

  1. muss mit einer vollständigen Karte abgeschlossen werden
  2. Versuchen Sie nicht die Vergleicher oben auf einer TreeMap; Es ist sinnlos, einen eingefügten Schlüssel zu vergleichen, wenn er erst nach dem Put einen Wert hat, d. h. er wird sehr schnell brechen

Punkt 1 ist für mich eine Art Dealbreaker. Google Collections ist unglaublich faul (was gut ist: Sie können praktisch jede Operation sofort ausführen; die eigentliche Arbeit wird erledigt, wenn Sie mit dem Ergebnis beginnen). Dazu müssen Sie eine ganze-Map kopieren!

"Vollständige" Antwort/Live sortierte Karte nach Werten

Mach dir keine Sorgen; Wenn Sie genug davon besessen sind, eine "Live" -Karte auf diese Weise sortiert zu haben, können Sie nicht nur eine, sondern beide (!) der oben genannten Probleme mit etwas Verrückten wie dem Folgenden lösen:

Hinweis: Dies hat sich im Juni 2012 erheblich geändert. Der vorherige Code konnte nie funktionieren: Eine interne HashMap ist erforderlich, um die Werte abzurufen, ohne eine Endlosschleife zwischen TreeMap.get() -> compare() und compare() -> get() zu erstellen.

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

Wenn wir setzen, stellen wir sicher, dass die Hash-Map den Wert für den Komparator hat, und legen Sie dann das TreeSet zum Sortieren an. Aber vorher überprüfen wir die Hash-Map, um zu sehen, dass der Schlüssel eigentlich kein Duplikat ist. Der von uns erstellte Komparator enthält auch den Schlüssel, damit doppelte Werte die nicht-doppelten Schlüssel nicht löschen (aufgrund von == Vergleich) .. Diese beiden Elemente sind vital, um das zu gewährleisten Kartenvertrag wird beibehalten; Wenn Sie glauben, dass Sie das nicht wollen, sind Sie fast an der Stelle, die Karte komplett umzukehren (zu Map<V,K>).

Der Konstruktor müsste als aufgerufen werden 

 new ValueComparableMap(Ordering.natural());
 //or
 new ValueComparableMap(Ordering.from(comparator));
208
Stephen

Von 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;
}
180
devinmoore

Mit Java 8 können Sie die streams api verwenden, um es wesentlich weniger ausführlich zu machen:

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

Um die Schlüssel zu sortieren, muss der Comparator jeden Wert für jeden Vergleich nachschlagen. Eine besser skalierbare Lösung würde das entrySet direkt verwenden, da dann der Wert sofort für jeden Vergleich verfügbar wäre (obwohl ich dies nicht durch Zahlen gesichert habe).

Hier ist eine generische Version von so etwas:

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 Gedächtnisrotation für die obige Lösung zu verringern. Die erste erstellte ArrayList kann beispielsweise als Rückgabewert verwendet werden. Dies würde die Unterdrückung einiger generischer Warnungen erfordern, aber es könnte sich für wiederverwendbaren Bibliothekscode lohnen. Der Comparator muss auch 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 ständig auf die sortierten Informationen zugreifen müssen (anstatt sie nur ab und zu zu sortieren), können Sie eine zusätzliche Multi-Map verwenden. Lass es mich wissen, wenn du mehr Details brauchst ...

30
volley

Die Commons-Collections-Bibliothek enthält eine Lösung namens TreeBidiMap . Oder schauen Sie sich die Google Collections-API an. Es hat TreeMultimap das Sie verwenden könnten.

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

25
p3t0r

Ich habe mir die gegebenen Antworten angesehen, aber viele von ihnen sind komplizierter als nötig oder entfernen Mapelemente, 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 wird.

24
Anthony

Um dies mit den neuen Funktionen in Java 8 zu erreichen:

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

Die Einträge werden mit dem angegebenen Vergleicher nach ihren Werten sortiert. Wenn Ihre Werte jedoch miteinander vergleichbar sind, ist kein expliziter Vergleich nötig:

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

Die zurückgegebene Liste ist eine Momentaufnahme der angegebenen Map zum Zeitpunkt, zu dem diese Methode aufgerufen wird, und spiegelt somit keine Änderungen an der anderen wider. Für eine iterierbare Live-Ansicht der Karte:

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

Das zurückgegebene iterable erstellt jedes Mal, wenn es iteriert wird, eine neue Momentaufnahme der angegebenen Karte. Wenn Sie es nicht gleichzeitig ändern, wird immer der aktuelle Status der Karte angezeigt.

17
gdejohn

Ich bin zwar einig, dass die ständige Notwendigkeit, eine Karte zu sortieren, wahrscheinlich ein Geruch ist, aber ich denke, der folgende Code ist der einfachste Weg, dies ohne eine andere Datenstruktur zu tun.

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

}

Und hier ist ein peinlich unvollständiger 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());
}

}

Das Ergebnis ist eine sortierte Liste von Map.Entry-Objekten, aus der Sie die Schlüssel und Werte abrufen können.

14
Lyudmil

Erstellen Sie einen benutzerdefinierten Komparator und verwenden Sie ihn beim Erstellen eines neuen TreeMap-Objekts.

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

    }
}

Verwenden Sie den folgenden Code in Ihrer Hauptfunktion

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

Ausgabe:

{B=75, D=50, C=50, A=35}
14
Sujan Reddy A

Verwenden Sie einen generischen Vergleicher wie:

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

    private Map<K,V> map;

    private MapValueComparator() {
        super();
    }

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

    public int compare(K o1, K o2) {
        return map.get(o1).compareTo(map.get(o2));
    }
}
11
RoyalBigorno

Die Antwort, für die am meisten abgestimmt wurde, funktioniert nicht, wenn Sie zwei Elemente haben, die gleich ..__ sind. Die TreeMap lässt gleiche Werte aus.

das Beispiel: unsortierte Karte

 Schlüssel/Wert: D/67.3 
 Schlüssel/Wert: A/99.5 
 Schlüssel/Wert: B/67.4 
 Schlüssel/Wert: C/67.5 
 Schlüssel/Wert: E/99.5 

ergebnisse

 Schlüssel/Wert: A/99.5 
 Schlüssel/Wert: C/67.5 
 Schlüssel/Wert: B/67.4 
 Schlüssel/Wert: D/67.3 

Also lässt E weg !!

Für mich hat es gut funktioniert, den Komparator anzupassen, wenn er nicht gleich 0 sondern -1 zurückgibt.

im Beispiel:

die Klasse ValueComparator implementiert Comparator {

Kartenbasis; public ValueComparator (Kartenbasis) { this.base = Basis; }

public int compare (Objekt a, Objekt 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;
}

} }

jetzt kehrt es zurück:

unsortierte Karte:

 Schlüssel/Wert: D/67.3 
 Schlüssel/Wert: A/99.5 
 Schlüssel/Wert: B/67.4 
 Schlüssel/Wert: C/67.5 
 Schlüssel/Wert: E/99.5 

ergebnisse:

 Schlüssel/Wert: A/99.5 
 Schlüssel/Wert: E/99.5 
 Schlüssel/Wert: C/67.5 
 Schlüssel/Wert: B/67.4 
 Schlüssel/Wert: D/67.3 

als Antwort auf Aliens (22. Nov. 2011): Ich benutze diese Lösung für eine Karte mit Integer-IDs und Namen, aber die Idee ist die gleiche, daher könnte der obige Code nicht korrekt sein (ich werde es schreiben.) In einem Test (und geben Sie den richtigen Code an), ist dies der Code für eine Map-Sortierung, basierend auf der obigen Lösung:

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


}

und dies ist die Testklasse (ich habe es gerade getestet, und dies funktioniert für die Ganzzahl, 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);
    }
}

hier ist der Code für den Komparator einer Karte:

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

und das ist der Testfall dafür:

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

natürlich können Sie dies viel mehr generisch machen, aber ich brauchte es nur für 1 Fall (die Karte)

11
michel.iamit

Anstatt Collections.sort zu verwenden, würde ich vorschlagen, Arrays.sort zu verwenden. Was Collections.sort tatsächlich macht, ist ungefähr so:

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

Es ruft einfach toArray in der Liste auf und verwendet dann Arrays.sort. Auf diese Weise werden alle Map-Einträge dreimal kopiert: Einmal von der Map in die temporäre Liste (entweder LinkedList oder ArrayList), dann in das temporäre Array und schließlich in die neue Map.

Meine Lösung lässt diesen einen Schritt zu, da sie keine unnötige LinkedList erstellt. Hier ist der Code, generischfreundlich und leistungsoptimiert:

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
ciamej

Dies ist eine Variante der Antwort von Anthony, die bei doppelten Werten nicht funktioniert:

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

Beachten Sie, dass der Umgang mit Nullen eher in der Luft liegt. 

Ein wichtiger Vorteil dieses Ansatzes ist, dass er im Gegensatz zu einigen anderen hier angebotenen Lösungen tatsächlich eine Map zurückgibt.

8
Roger

Hauptproblem. Wenn Sie die erste Antwort verwenden (Google führt Sie hierher), ändern Sie den Vergleicher, um eine Gleichheitsbestimmung hinzuzufügen. Andernfalls können Sie keine Werte aus der sortierten_map über Schlüssel abrufen:

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
cuneyt

Bester Ansatz

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

Ausgabe

Java ==== 20

MAC ==== 26

C++ ==== 45

Unix ==== 67

Why this kolavari ==== 93
6
Nilesh Jadav

Es gibt bereits viele Antworten auf diese Frage, aber keine gab mir das, wonach ich suchte, eine Kartenimplementierung, die nach dem zugehörigen Wert sortierte Schlüssel und Einträge zurückgibt und diese Eigenschaft beibehält, wenn Schlüssel und Werte in der Karte geändert werden. Zwei andereFragen fragen Sie speziell. 

Ich habe ein allgemeines freundliches Beispiel zusammengestellt, das diesen Anwendungsfall löst. Diese Implementierung erfüllt nicht alle Verträge der Map-Benutzeroberfläche, z. B. das Anzeigen von Wertänderungen und Entfernungen in den Sets, die von keySet () und entrySet () im ursprünglichen Objekt zurückgegeben werden. Ich dachte, eine solche Lösung wäre zu groß, um sie in eine Stack Overflow-Antwort aufzunehmen. Wenn es mir gelingt, eine vollständigere Implementierung zu erstellen, werde ich sie vielleicht bei Github posten und dann in einer aktualisierten Version dieser Antwort darauf verlinken.

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

Verwenden Sie je nach Kontext Java.util.LinkedHashMap<T>, um die Reihenfolge zu speichern, in der Elemente in die Karte eingefügt werden. Wenn Sie die Werte nach ihrer natürlichen Reihenfolge sortieren müssen, würde ich andernfalls empfehlen, eine separate Liste zu pflegen, die über Collections.sort() sortiert werden kann.

5
Ryan Delucchi

DaTreeMap <>für Werte, die gleich sein können, nicht funktioniert, habe ich Folgendes verwendet:

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

Vielleicht möchten Sielistin eineLinkedHashMapsetzen, aber wenn Sie nur gleich iterieren, ist das überflüssig ...

5
malix

Das ist einfach zu kompliziert. Karten sollten nicht dazu dienen, sie nach Wert zu sortieren. Der einfachste Weg ist, eine eigene Klasse zu erstellen, die zu Ihrer Anforderung passt.

Im unteren Beispiel soll TreeMap an der Stelle, an der * steht, einen Vergleicher hinzugefügt werden. Über die Java-API werden jedoch nur Vergleichswerte und keine Vergleichswerte angegeben. Alle hier genannten Beispiele basieren auf 2 Karten. Ein Hash und ein neuer Baum. Was ist seltsam.

Das Beispiel:

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

So ändern Sie die Karte in einen Satz auf folgende Weise:

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

Sie erstellen die Klasse 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;
    }
}

und die Comparator-Klasse:

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

Auf diese Weise können Sie problemlos weitere Abhängigkeiten hinzufügen.

Als letzten Punkt füge ich einen einfachen Iterator hinzu:

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
Darkless

Afaik ist der sauberste Weg, Sammlungen zu verwenden, um die Karte nach Wert zu sortieren:

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
lisak

Hier ist eine OO -Lösung (d. H. Verwendet keine static-Methoden):

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

Hiermit an die Public Domain gespendet.

4
Dave Jarvis

Einige einfache Änderungen, um eine sortierte Karte mit Paaren mit doppelten Werten zu erhalten. Geben Sie in der Compare-Methode (Klasse ValueComparator) bei Werten gleich 0 nicht zurück, sondern geben Sie das Ergebnis des Vergleichs der 2 Schlüssel zurück. Schlüssel sind in einer Map eindeutig, so dass Sie doppelte Werte beibehalten können (die übrigens nach Schlüsseln sortiert werden). Das obige Beispiel könnte also folgendermaßen modifiziert werden:

    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
dimkar

Basierend auf @devinmoore-Code, einer Methode zur Kartensortierung, die Generics verwendet und sowohl aufsteigend als auch absteigend unterstützt.

/**
 * 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
Maxim Veksler

Sicher ist die Lösung von Stephen wirklich großartig, aber für diejenigen, die Guava nicht verwenden können:

Hier ist meine Lösung zum Sortieren einer Karte nach Wert. Diese Lösung behandelt den Fall, dass doppelt so viele Werte vorliegen usw.

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

Der Exec: http://www.ideone.com/dq3Lu

Die Ausgabe:

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

Ich hoffe es hilft einigen Leuten

4

Sie können Guavas Multimaps ausprobieren:

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

Als Ergebnis erhalten Sie eine Zuordnung von Originalwerten zu Sammlungen von Schlüsseln, die diesen entsprechen. Dieser Ansatz kann auch verwendet werden, wenn für denselben Wert mehrere Schlüssel vorhanden sind.

3

Späte Eintragung.

Mit dem Aufkommen von Java-8 können wir Streams auf sehr einfache/prägnante Weise für die Datenmanipulation verwenden. Sie können Streams verwenden, um die Map-Einträge nach Wert zu sortieren und eine LinkedHashMap zu erstellen, die die Einfügungsreihenfolge -Eteration beibehält.

Z.B:

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

Ersetzen Sie für die umgekehrte Reihenfolge:

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

mit

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()
3
Pankaj Singhal

Ich habe die Lösungen von user157196 und Carter Page zusammengeführt:

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
RobotMan

Wenn Sie über doppelte Schlüssel und nur einen kleinen Datensatz (<1000) verfügen und Ihr Code nicht leistungskritisch ist, können Sie Folgendes tun:

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 ist die Eingabe für den Code.

Die Variable sortiertOutputMap enthält die Daten in absteigender Reihenfolge, wenn sie wiederholt wird. Um die Reihenfolge zu ändern, ändern Sie in der if-Anweisung einfach> in ein <.

Ist nicht die schnellste Sortierung, erledigt den Job jedoch ohne zusätzliche Abhängigkeiten.

3
nibor

Wenn ich damit konfrontiert bin, erstelle ich eine Liste nebenan. Wenn Sie sie in einer benutzerdefinierten Map-Implementierung zusammenstellen, hat dies ein schönes Gefühl ... Sie können so etwas wie das Folgende verwenden und die Sortierung nur bei Bedarf durchführen. (Hinweis: Ich habe das nicht wirklich getestet, aber es wird kompiliert ... könnte irgendwo ein dummer kleiner Fehler sein)

(Wenn Sie möchten, dass es nach Schlüsseln und Werten sortiert wird, lassen Sie die Klasse TreeMap erweitern, definieren Sie keine Accessor-Methoden und lassen Sie die Mutatoren super.xxxxx anstelle von map_.xxxx aufrufen.

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

Diese Methode wird nur dem Zweck dienen. (Das 'Zurücksetzen' ist, dass die Werte die Java.util.Comparable-Schnittstelle implementieren müssen).

  /**

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

Hier ist der Code von Java 8 mit 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}

Erklärung: Ich bin der Entwickler von AbacusUtil.

2
user_3380739

posten meiner Antwortversion

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

Die einfachste brute-force sortHashMap-Methode für HashMap<String, Long>: Sie können nur copypaste und verwenden wie folgt:

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

Ich habe die Methode von devinmoore neu geschrieben, mit der eine Karte nach ihrem Wert sortiert wird, ohne Iterator zu verwenden:

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:, dass wir LinkedHashMap als Ausgabekarte verwendet haben, da unsere Liste nach Wert sortiert wurde, und wir sollten unsere Liste in der Ausgabekarte mit der Reihenfolge der eingefügten Schlüssel und Werte speichern. Wenn Sie beispielsweise TreeMap als Ausgabekarte verwenden, wird Ihre Karte erneut nach Kartenschlüsseln sortiert!

Dies ist die Hauptmethode:

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

Schließlich ist dies die Ausgabe:

Input Map:{1=one, 3=three, 5=five}
Sorted Map:{5=five, 1=one, 3=three}
0
smart-developer

Meine Lösung ist ein recht einfacher Ansatz, um die meist angegebenen APIs zu verwenden. Wir verwenden die Funktion Map , um ihren Inhalt als Set via entrySet () - Methode zu exportieren. Wir haben jetzt ein Set , das Map.Entry Objekte enthält. 

Okay, ein Set führt keine Bestellung aus, aber wir können den Inhalt in eine ArrayList aufnehmen. Es hat jetzt eine random Reihenfolge, aber wir werden es trotzdem sortieren.

Da ArrayList eine Collection ist, verwenden wir jetzt die Collections.sort () - Methode, um Ordnung ins Chaos zu bringen. Da unsere Map.Entry - Objekte nicht die Art von Vergleich realisieren, die wir benötigen, stellen wir einen benutzerdefinierten Comparator zur Verfügung.

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
Alexander

Verwenden der Guava-Bibliothek:

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

AUSGABE =

//sort by values 

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

Wenn es eine Präferenz für eine Map-Datenstruktur gibt, die inhärent nach Werten sortiert wird, ohne dass Sortiermethoden ausgelöst oder explizit an ein Dienstprogramm übergeben werden müssen, können die folgenden Lösungen zutreffen:

(1) org.drools.chance.core.util.ValueSortedMap (JBoss-Projekt) verwaltet intern zwei Karten, eine für die Suche und eine für die Verwaltung der sortierten Werte. Ganz ähnlich wie zuvor hinzugefügte Antworten, aber wahrscheinlich ist es der Teil Abstraktion und Einkapselung (einschließlich des Kopiermechanismus), der die Verwendung von außen sicherer macht. 

(2) http://techblog.molindo.at/2008/11/Java-map-sorted-by-value.html vermeidet die Verwaltung von zwei Karten und verlässt sich stattdessen auf die LinkedMap von Apache Common. (Anmerkung des Blogautors: 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) Schreiben Sie eine benutzerdefinierte Map oder erstreckt sich von LinkedHashMap, die nur während der Aufzählung sortiert wird (z. B. values(), keyset(), entryset()). Die innere Implementierung/das innere Verhalten wird von der Verwendung dieser Klasse abstrahiert, der Client dieser Klasse scheint jedoch, dass Werte immer sortiert werden, wenn sie zur Auflistung aufgefordert werden. Diese Klasse hofft, dass die Sortierung meistens einmal erfolgt, wenn alle put-Vorgänge vor der Aufzählung abgeschlossen wurden. Die Sortiermethode übernimmt einige der vorherigen Antworten auf diese Frage. 

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 bietet ImmutableMap.Builder.orderEntriesByValue (Comparator valueComparator) obwohl die resultierende Karte unveränderlich ist:

Konfiguriert diesen Generator, um Einträge nach dem Wert nach .__ zu ordnen. spezifizierter Komparator.

Die Sortierreihenfolge ist stabil, wenn zwei Einträge den Wert .__ haben. Vergleiche als gleichwertig, der Eintrag, der zuerst eingefügt wurde, ist der erste in der Iterationsreihenfolge der erstellten Karte.

0
Kenston Choi