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.

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.


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.

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

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 !


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

36

Wystarczy użyć TreeMap

new TreeMap<String, String>(unsortMap);

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


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

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

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

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

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

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

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

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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.