Wie sortiere ich Map-Werte nach Schlüssel in Java?


362

Ich habe eine Map, die Zeichenfolgen für Schlüssel und Werte enthält.

Die Daten sind wie folgt:

Frage 1, 1
Frage 9, 1
Frage 2, 4
Frage 5, 2

Ich möchte die Karte anhand ihrer Schlüssel sortieren. Also, am Ende werde ich question1, question2, question3... und so weiter haben.


Schließlich versuche ich, zwei Zeichenfolgen aus dieser Karte herauszuholen.

  • Erster String: Fragen (in der Reihenfolge 1 ..10)
  • Zweiter String: Antworten (in derselben Reihenfolge wie die Frage)

Im Moment habe ich folgendes:

Iterator it = paramMap.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry pairs = (Map.Entry) it.next();
    questionAnswers += pairs.getKey() + ",";
}

Das bringt mir die Fragen in eine Reihe, aber sie sind nicht in Ordnung.

Antworten:


613

Kurze Antwort

Verwenden Sie a TreeMap. Genau dafür ist es.

Wenn diese Karte an Sie übergeben wird und Sie den Typ nicht bestimmen können, können Sie Folgendes tun:

SortedSet<String> keys = new TreeSet<>(map.keySet());
for (String key : keys) { 
   String value = map.get(key);
   // do something
}

Dies wird in der natürlichen Reihenfolge der Schlüssel über die Karte iteriert.


Längere Antwort

Technisch gesehen können Sie alles verwenden, was implementiert wird SortedMap, aber in seltenen Fällen beträgt dies TreeMapgenau so, wie es bei der Verwendung einer MapImplementierung normalerweise der Fall ist HashMap.

In Fällen, in denen Ihre Schlüssel ein komplexer Typ sind, der Comparable nicht implementiert, oder Sie dann nicht die natürliche Reihenfolge verwenden möchten TreeMapund TreeSetüber zusätzliche Konstruktoren verfügen, mit denen Sie Folgendes übergeben können Comparator:

// placed inline for the demonstration, but doesn't have to be a lambda expression
Comparator<Foo> comparator = (Foo o1, Foo o2) -> {
        ...
    }

SortedSet<Foo> keys = new TreeSet<>(comparator);
keys.addAll(map.keySet());

Denken Sie bei der Verwendung eines TreeMapoder daran, TreeSetdass es andere Leistungsmerkmale als HashMapoder aufweist HashSet. Grob gesagt gehen Operationen, die ein Element finden oder einfügen, von O (1) nach O (Log (N)) .

In a HashMapwirkt sich das Verschieben von 1000 auf 10.000 Elemente nicht wirklich auf Ihre Zeit zum Nachschlagen eines Elements aus, aber für a ist TreeMapdie Nachschlagezeit etwa dreimal langsamer (unter der Annahme von Protokoll 2 ). Das Verschieben von 1000 auf 100.000 ist bei jeder Elementsuche etwa sechsmal langsamer.


Ich versuche, Treemap zu verwenden und String-Schlüssel nach Länge zu sortieren. Ich erhalte inkonsistente Abrufergebnisse. Anscheinend, weil TreeMap ein compareTo-Ergebnis von 0 als "gleich" betrachtet? Ich bin mir nicht sicher, wie ich es in diesem Fall verwenden soll.
Marc

1
compareTo () Ergebnis von 0 ist 'gleich'. Wenn Sie einen Komparator schreiben, der nach Zeichenfolgenlänge sortiert, müssen Sie einen positiven oder negativen Wert zurückgeben, der darauf basiert, welche Zeichenfolge länger ist, und nur 0 zurückgeben, wenn beide Zeichenfolgen dieselbe Länge haben. Wenn a und b Zeichenfolgen sind, können Sie dies wie folgt tun: "return a.length () - b.length ()" (oder die Werte umkehren, wenn Sie möchten, dass sie in die andere Richtung sortiert werden).
Jherico

Hallo Leute, wenn er / sie die Karte nach Schlüsseln bestellen möchte, was hier 1,2,3,4 ist, wie lautet die Einfügereihenfolge ... warum verwenden wir nicht LinkedHashSet? Wir stellen die Fragen einfach einzeln und sie werden in der Reihenfolge der Einfügung sortiert. Können mir einige dabei helfen?
Karoly

@Karoly LinkedHashSet würde funktionieren, um die Elemente in der Reihenfolge abzurufen, in der Sie sie eingefügt haben. Das OP möchte die Elemente unabhängig von der Einfügereihenfolge in einer vorgegebenen Sortierreihenfolge abrufen.
David Berry

@ Cricket_007 Der Code zeigt speziell, wie die Schlüssel für eine Karte durchlaufen werden, die noch nicht sortiert ist.
Jherico

137

Angenommen, TreeMap ist nicht gut für Sie (und vorausgesetzt, Sie können keine Generika verwenden):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.

3
Vielen Dank! Ich musste so etwas tun, da meine Schlüssel ein komplexer Typ waren.
Ross Hambrick

3
Dadurch wird nur die Schlüsselliste sortiert, die Karte selbst wird jedoch nicht anhand der Schlüssel sortiert. Ich suche auch, wie man die Karte anhand der Schlüssel sortiert und einen Weg findet. Ich werde es wohl mit TreeMap versuchen müssen :)
Crenguta S

55

Mit TreeMapkönnen Sie die Karte sortieren.

Map<String, String> map = new HashMap<>();        
Map<String, String> treeMap = new TreeMap<>(map);
for (String str : treeMap.keySet()) {
    System.out.println(str);
}

1
Map <String, List <String >> treeMap = new TreeMap <String, List <String >> (printHashMap); for (String str: treeMap.keySet ()) {System.out.println (str + "" + treeMap.get (str)); }
Vikramvi

37

Verwenden Sie eine TreeMap !


31
+1 - Nicht agil genug, um Jherico, Jon, zu schlagen, aber immer noch ziemlich gut. 8)
Duffymo

Ich kenne Java nicht :-( Das funktioniert zu 100%. Viel einfacher als jede der schrecklichen Lösungen, die ich mir
ausgedacht habe

36

Wenn Sie bereits eine Karte haben und diese nach Schlüsseln sortieren möchten, verwenden Sie einfach:

Map<String, String> treeMap = new TreeMap<String, String>(yourMap);

Ein vollständiges Arbeitsbeispiel:

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

class SortOnKey {

public static void main(String[] args) {
   HashMap<String,String> hm = new HashMap<String,String>();
   hm.put("3","three");
   hm.put("1","one");
   hm.put("4","four");
   hm.put("2","two");
   printMap(hm);
   Map<String, String> treeMap = new TreeMap<String, String>(hm);
   printMap(treeMap);
}//main

public static void printMap(Map<String,String> map) {
    Set s = map.entrySet();
    Iterator it = s.iterator();
    while ( it.hasNext() ) {
       Map.Entry entry = (Map.Entry) it.next();
       String key = (String) entry.getKey();
       String value = (String) entry.getValue();
       System.out.println(key + " => " + value);
    }//while
    System.out.println("========================");
}//printMap

}//class

36

Verwenden Sie einfach TreeMap

new TreeMap<String, String>(unsortMap);

Beachten Sie, dass die TreeMap nach der natürlichen Reihenfolge ihrer 'Schlüssel' sortiert ist.


19

Sofern Sie nicht verwenden können TreeMap, können wir in Java 8 die toMap () -Methode verwenden, Collectorsdie folgende Parameter verwendet:

  • Keymapper : Mapping-Funktion zur Erzeugung von Schlüsseln
  • valueemapper : Zuordnungsfunktion zur Erzeugung von Werten
  • mergeFunction : Eine Zusammenführungsfunktion, mit der Kollisionen zwischen Werten aufgelöst werden, die demselben Schlüssel zugeordnet sind
  • mapSupplier : Eine Funktion, die eine neue, leere Map zurückgibt, in die die Ergebnisse eingefügt werden.

Java 8 Beispiel

Map<String,String> sample = new HashMap<>();  // push some values to map  
Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                    .sorted(Map.Entry.<String,String>comparingByKey().reversed())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
Map<String, String> newMapSortedByValue = sample.entrySet().stream()
                        .sorted(Map.Entry.<String,String>comparingByValue().reversed())
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));

Wir können das Beispiel ändern, um einen benutzerdefinierten Komparator zu verwenden und nach Schlüsseln zu sortieren:

Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                .sorted((e1,e2) -> e1.getKey().compareTo(e2.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));

funktioniert nur in Android mit Api 24 und höher.
Tina

9

Verwenden von Java 8:

Map<String, Integer> sortedMap = unsortMap.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));

5

Dieser Code kann eine Schlüsselwertzuordnung in beiden Reihenfolgen sortieren, dh aufsteigend und absteigend.

<K, V extends Comparable<V>> Map<K, V> sortByValues
     (final Map<K, V> map, int ascending)
{
     Comparator<K> valueComparator =  new Comparator<K>() {         
        private int ascending;
        public int compare(K k1, K k2) {
            int compare = map.get(k2).compareTo(map.get(k1));
            if (compare == 0) return 1;
            else return ascending*compare;
        }
        public Comparator<K> setParam(int ascending)
        {
            this.ascending = ascending;
            return this;
        }
    }.setParam(ascending);

    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

Als Beispiel:

Map<Integer,Double> recommWarrVals = new HashMap<Integer,Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals,-1);  // Descending order

5

In Java 8

Um einen Map<K, V>nach Schlüssel zu sortieren , geben Sie die Schlüssel in einen List<K>:

List<K> result = map.keySet().stream().sorted().collect(Collectors.toList());

So sortieren Sie einen Map<K, V>nach Schlüssel: Fügen Sie Einträge in ein List<Map.Entry<K, V>>:

List<Map.Entry<K, V>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey())
       .collect(Collectors.toList());

Last but not least: Um Zeichenfolgen länderspezifisch zu sortieren, verwenden Sie eine Collator- Klasse (Komparatorklasse):

Collator collator = Collator.getInstance(Locale.US);
collator.setStrength(Collator.PRIMARY); // case insensitive collator

List<Map.Entry<String, String>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey(collator))
       .collect(Collectors.toList());

1
List<String> list = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
for (String str : map.keySet()) {
 list.add(str);
}
Collections.sort(list);
for (String str : list) {
 System.out.println(str);
}

1

In Java 8 können Sie auch .stream (). Sorted () verwenden:

myMap.keySet().stream().sorted().forEach(key -> {
        String value = myMap.get(key);

        System.out.println("key: " + key);
        System.out.println("value: " + value);
    }
);

0

Wir können den Schlüssel auch mithilfe der Arrays.sort-Methode sortieren.

Map<String, String> map = new HashMap<String, String>();
Object[] objArr = new Object[map.size()];
for (int i = 0; i < map.size(); i++) {
objArr[i] = map.get(i);
}
Arrays.sort(objArr);
for (Object str : objArr) {
System.out.println(str);
}

Dies ist eine Sortierung nach Wert und nicht nach Schlüssel.
Raaj

0

Nur für den Fall, dass Sie keine verwenden möchten TreeMap

public static Map<Integer, Integer> sortByKey(Map<Integer, Integer> map) {
    List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
    list.sort(Comparator.comparingInt(Map.Entry::getKey));
    Map<Integer, Integer> sortedMap = new HashMap<>();
    list.forEach(sortedMap.put(e.getKey(), e.getValue()));
    return sortedMap;
}

Auch im Fall wollten Sie Ihre Karte auf der Grundlage der sortieren valuesgerade Änderung Map.Entry::getKeyzuMap.Entry::getValue


Dies wurde in meinem Überprüfungsprotokoll angezeigt. Jemand hat versucht, einige Fehler mit Variablennamen zu beheben (ist jedoch fehlgeschlagen). Also habe ich sie richtig behoben, aber diese Antwort ist einfach grundlegend falsch. Die Reihenfolge, in der Sie einer HashMap Werte hinzufügen, spielt keine Rolle. Karten haben keine Bestellung. stackoverflow.com/questions/10710193/…
aepryus
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.