Come ordinare i valori della mappa per chiave in Java?


362

Ho una mappa che ha stringhe per chiavi e valori.

I dati sono i seguenti:

"question1", "1"
"question9", "1"
"question2", "4"
"question5", "2"

Voglio ordinare la mappa in base alle sue chiavi. Quindi, alla fine, avrò question1, question2, question3.... e così via.


Alla fine, sto cercando di ottenere due stringhe da questa mappa.

  • Prima stringa: domande (nell'ordine 1 ..10)
  • Seconda stringa: Risposte (nello stesso ordine della domanda)

In questo momento ho il seguente:

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

Questo mi dà le domande in una stringa ma non sono in ordine.

Risposte:


613

Risposta breve

Usa a TreeMap. Questo è esattamente ciò che serve.

Se questa mappa ti viene passata e non è possibile determinare il tipo, è possibile effettuare le seguenti operazioni:

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

Questo ripeterà la mappa in ordine naturale delle chiavi.


Risposta più lunga

Tecnicamente, puoi usare tutto ciò che implementa SortedMap, ma salvo rari casi questo equivale a TreeMap, così come lo è di Mapsolito usare un'implementazione HashMap.

Per i casi in cui le tue chiavi sono di un tipo complesso che non implementa Comparable o non vuoi usare l'ordine naturale allora TreeMape TreeSethai costruttori aggiuntivi che ti permettono di passare in un 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());

Ricorda quando usi a TreeMapo TreeSetche avrà caratteristiche prestazionali diverse da HashMapo HashSet. In parole povere, le operazioni che trovano o inseriscono un elemento passeranno da O (1) a O (Log (N)) .

In un HashMappassaggio da 1000 elementi a 10.000 non influisce sul tempo di ricerca di un elemento, ma per un TreeMaptempo di ricerca sarà circa 3 volte più lento (presupponendo Log 2 ). Il passaggio da 1000 a 100.000 sarà circa 6 volte più lento per ogni ricerca di elementi.


Sto cercando di usare Treemap e ordinare le chiavi String in base alla lunghezza. Trovo che sto ottenendo risultati di recupero incoerenti. Apparentemente perché TreeMap considera un confrontoPer il risultato di 0 come "uguale"? Non sono sicuro di come usarlo in questo caso.
Marc,

1
Il risultato di compareTo () di 0 è 'uguale a'. Se stai scrivendo un comparatore che ordina in base alla lunghezza della stringa, devi restituire un valore positivo o negativo in base alla stringa più lunga e restituire 0 solo se entrambe le stringhe hanno la stessa lunghezza. se aeb sono stringhe puoi farlo in questo modo `return a.length () - b.length () '(o invertire i valori se li vuoi ordinare nell'altra direzione).
Jherico,

Ciao ragazzi, se desidera che la mappa venga ordinata con le chiavi, che qui è 1,2,3,4, qual è l'ordine di inserimento .... perché non stiamo usando LinkedHashSet? Mettiamo le domande una alla volta e vengono ordinate in base all'ordine dell'inserimento. Qualcuno può darmi una mano con questo?
Karoly,

@Karoly LinkedHashSet avrebbe funzionato per recuperare gli elementi nell'ordine in cui li hai inseriti. Ciò che l'OP desidera è recuperare gli elementi in un ordinamento predeterminato, indipendentemente dall'ordine di inserzione.
David Berry,

@ cricket_007 il codice sta dimostrando in modo specifico come scorrere le loro chiavi per una mappa che non è già ordinata.
Jherico

137

Supponendo che TreeMap non sia buono per te (e supponendo che non puoi usare generici):

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

3
Grazie! Avevo bisogno di fare qualcosa del genere poiché le mie chiavi erano di tipo complesso.
Ross Hambrick,

3
Questo ordinerà semplicemente l'elenco delle chiavi, ma non ordinerà la mappa stessa in base alle chiavi. Sto anche cercando come ordinare la mappa in base ai tasti e trovare un modo. Dovrò provare con TreeMap immagino :)
Crenguta S

55

Usando TreeMapè possibile ordinare la mappa.

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

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

37

Usa una TreeMap !


31
+1 - Non abbastanza agile per battere Jherico, Jon, ma comunque abbastanza buono. 8)
duffymo,

Non conosco Java :-( Funziona al 100%. Molto più semplice di qualsiasi delle orribili soluzioni che mi sono venute in mente
peterchaula

36

Se hai già una mappa e desideri ordinarla sui tasti, utilizza semplicemente:

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

Un esempio di lavoro completo:

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

Usa TreeMap

new TreeMap<String, String>(unsortMap);

Tieni presente che la TreeMap è ordinata in base all'ordinamento naturale delle sue "chiavi"


19

A condizione che tu non possa usare TreeMap, in Java 8 possiamo usare il metodo toMap () in Collectorscui accetta i seguenti parametri:

  • keymapper : funzione di mappatura per produrre chiavi
  • valuemapper : funzione di mappatura per produrre valori
  • mergeFunction : una funzione di unione, utilizzata per risolvere le collisioni tra valori associati alla stessa chiave
  • mapSupplier : una funzione che restituisce una nuova mappa vuota in cui verranno inseriti i risultati.

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

Possiamo modificare l'esempio per utilizzare il comparatore personalizzato e ordinare in base alle chiavi come:

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

funziona solo su Android con Api 24 e versioni successive.
Tina,

10

Utilizzando 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

Questo codice può ordinare una mappa di valori-chiave in entrambi gli ordini, cioè crescente o decrescente.

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

Come esempio:

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

5

In Java 8

Per ordinare una Map<K, V>chiave, inserendo le chiavi in ​​una List<K>:

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

Per ordinare una Map<K, V>chiave, inserendo le voci in una List<Map.Entry<K, V>>:

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

Ultimo ma non meno importante: per ordinare le stringhe in modo sensibile alle impostazioni locali - utilizzare una classe Collator (comparatore):

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 puoi anche usare .stream (). Sort ():

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

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

0

È inoltre possibile ordinare la chiave utilizzando il metodo 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);
}

Questo è l'ordinamento per valore e non per chiave.
raaj,

0

Nel caso in cui non desideri utilizzare a 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;
}

Inoltre, nel caso in cui volessi ordinare la tua mappa basandoti valuessemplicemente Map.Entry::getKeysuMap.Entry::getValue


Questo è emerso nel mio registro delle recensioni. Qualcuno ha provato a correggere alcuni errori con i nomi delle variabili (ma non è riuscito). Quindi, li ho riparati correttamente, tuttavia, questa risposta è fondamentalmente sbagliata. L'ordine in cui si aggiungono valori a una HashMap è irrilevante. Le mappe non hanno ordine. stackoverflow.com/questions/10710193/...
Aepryus
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.