Jak sortować wartości map według klucza w Javie?

361

Mam mapę, która ma ciągi znaków zarówno dla kluczy, jak i wartości.

Dane są następujące:

„pytanie 1”, „1”
„pytanie 9”, „1”
„pytanie 2”, „4”
„pytanie 5”, „2”

Chcę posortować mapę według jej kluczy. Więc w końcu będę miał question1, question2, question3... i tak dalej.


W końcu staram się uzyskać dwa ciągi z tej mapy.

  • Pierwszy ciąg: pytania (w kolejności 1 ..10)
  • Drugi ciąg: odpowiedzi (w tej samej kolejności co pytanie)

W tej chwili mam następujące:

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

To sprawia, że ​​pytania są w ciągu, ale nie są w porządku.

n00bstackie
źródło

Odpowiedzi:

612

Krótka odpowiedź

Użyj a TreeMap. Właśnie po to jest.

Jeśli ta mapa została Ci przekazana i nie możesz określić typu, możesz wykonać następujące czynności:

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

Spowoduje to iterację po mapie w naturalnej kolejności klawiszy.


Dłuższa odpowiedź

Technicznie rzecz biorąc, możesz użyć wszystkiego, co implementuje SortedMap, ale z wyjątkiem rzadkich przypadków jest to równoznaczne z TreeMapkorzystaniem z Mapimplementacji HashMap.

W przypadkach, w których klucze są złożonym typem, który nie implementuje Porównywalnego lub nie chcesz wtedy używać naturalnego porządku TreeMapi TreeSetmasz dodatkowych konstruktorów, które pozwalają przekazać 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());

Pamiętaj, gdy używasz a TreeMaplub TreeSet, że będzie on miał inną charakterystykę wydajności niż HashMaplub HashSet. Z grubsza mówiąc operacje, które wyszukują lub wstawiają element, przechodzą z O (1) do O (Log (N)) .

W ciągu a HashMapprzejście z 1000 przedmiotów do 10 000 tak naprawdę nie wpływa na czas wyszukiwania elementu, ale dla TreeMapczasu wyszukiwania będzie on około 3 razy wolniejszy (przy założeniu Log 2 ). Przejście z 1000 do 100 000 będzie około 6 razy wolniejsze dla każdego wyszukiwania elementu.

Jherico
źródło
Usiłuję użyć Treemap i posortować klucze String według długości. Uważam, że otrzymuję niespójne wyniki wyszukiwania. Najwyraźniej dlatego, że TreeMap uważa wynik porównania do 0 za „równy”? Nie wiem, jak go użyć w tym przypadku.
Marc
1
CompareTo () wynikiem 0 jest „równa się”. Jeśli piszesz komparator, który sortuje według długości łańcucha, musisz zwrócić wartość dodatnią lub ujemną na podstawie tego, który łańcuch jest dłuższy i zwrócić 0, jeśli oba łańcuchy są tej samej długości. jeśli a i b są łańcuchami, możesz to zrobić w następujący sposób: `a return a.length () - b.length () '(lub odwróć wartości, jeśli chcesz je posortować w innym kierunku).
Jherico
Witajcie, jeśli on / ona chciałby, aby mapa została uporządkowana według kluczy, czyli 1,2,3,4, jaka jest kolejność wstawiania .... dlaczego nie używamy LinkedHashSet? Po prostu zadajemy pytania jeden po drugim i porządkuje się je według kolejności wstawiania. Czy ktoś może mi w tym pomóc?
Karoly
@Karoly LinkedHashSet działałby w celu pobrania elementów w kolejności ich wstawienia. OP chce pobrać elementy w określonej kolejności sortowania, niezależnie od kolejności wstawiania.
David Berry,
@ cricket_007 kod pokazuje konkretnie, jak iterować po klawiszach mapy, która nie jest jeszcze posortowana.
Jherico
137

Zakładając, że TreeMap nie jest dla ciebie dobry (i zakładając, że nie możesz używać ogólnych):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.
TrayMan
źródło
3
Dzięki! Musiałem zrobić coś takiego, ponieważ moje klucze były skomplikowane.
Ross Hambrick
3
Spowoduje to jedynie posortowanie listy kluczy, ale nie posortuje samej mapy na podstawie kluczy. Zastanawiam się też, jak posortować mapę według kluczy i znaleźć sposób. Chyba będę musiał spróbować z TreeMap :)
Crenguta S
55

Za pomocą TreeMapmożesz posortować mapę.

Map<String, String> map = new HashMap<>();        
Map<String, String> treeMap = new TreeMap<>(map);
for (String str : treeMap.keySet()) {
    System.out.println(str);
}
Manoj Singh
źródło
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

Użyj TreeMap !

AgileJon
źródło
31
+1 - Niewystarczająco zwinny, aby pokonać Jherico, Jon, ale wciąż całkiem niezły. 8)
duffymo
Nie znam Java :-( Działa to w 100%. O wiele prostsze niż którekolwiek z okropnych rozwiązań, które wymyśliłem
peterchaula
36

Jeśli masz już mapę i chcesz ją posortować według kluczy, po prostu użyj:

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

Kompletny przykład działania:

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
MD
źródło
36

Wystarczy użyć TreeMap

new TreeMap<String, String>(unsortMap);

Pamiętaj, że TreeMap jest sortowany zgodnie z naturalną kolejnością „kluczy”

Aliti
źródło
19

Pod warunkiem, że nie możesz używać TreeMap, w Javie 8 możemy skorzystać z metody toMap () , Collectorsktóra przyjmuje następujące parametry:

  • keymapper : funkcja mapowania do tworzenia kluczy
  • valuemapper : funkcja mapowania do tworzenia wartości
  • mergeFunction : funkcja scalania, używana do rozwiązywania kolizji między wartościami powiązanymi z tym samym kluczem
  • mapSupplier : funkcja, która zwraca nową, pustą mapę, do której zostaną wstawione wyniki.

Przykład Java 8

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

Możemy zmodyfikować przykład, aby użyć niestandardowego komparatora i sortować na podstawie kluczy jako:

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));
akhil_mittal
źródło
działa tylko w Androidzie z Api 24 i nowszym.
Tina
9

Za pomocą 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));
Taras Melnyk
źródło
5

Ten kod może sortować mapę klucz-wartość w obu zamówieniach, tj. Rosnąco i malejąco.

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

Jako przykład:

Map<Integer,Double> recommWarrVals = new HashMap<Integer,Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals,-1);  // Descending order
M. Mashaye
źródło
5

W Javie 8

Aby posortować Map<K, V>według klucza, wkładając klucze do List<K>:

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

Aby posortować Map<K, V>według klucza, wstawiając wpisy do 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: do sortowania ciągów w sposób uwzględniający ustawienia regionalne - użyj klasy Collator (komparator):

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());
Oleksandr Pyrohov
źródło
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);
}
Manoj Singh
źródło
1

W Javie 8 możesz także użyć .stream (). Sorted ():

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

        System.out.println("key: " + key);
        System.out.println("value: " + value);
    }
);
Jonas_Hess
źródło
0

Możemy również posortować klucz za pomocą metody Arrays.sort.

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);
}
Manoj Singh
źródło
Jest to sortowanie według wartości, a nie według klucza.
raaj
0

Na wypadek, gdybyś nie chciał użyć 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;
}

Ponadto, w przypadku, gdy chciał, aby uporządkować swoje mapy na podstawie valuestylko zmiany Map.Entry::getKeydoMap.Entry::getValue

Ankit Sharma
źródło
To pojawiło się w moim dzienniku recenzji. Ktoś próbował naprawić niektóre błędy z nazwami zmiennych (ale nie powiodło się). Więc poprawiłem je poprawnie, jednak ta odpowiedź jest po prostu zła zasadniczo. Kolejność dodawania wartości do HashMap jest nieistotna. Mapy nie mają zamówienia. stackoverflow.com/questions/10710193/…
aepryus