Comment trier les valeurs de carte par clé en Java?

362

J'ai une carte qui a des chaînes pour les clés et les valeurs.

Les données sont comme suit:

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

Je veux trier la carte en fonction de ses clés. Donc, à la fin, j'aurai question1, question2, question3... et ainsi de suite.


Finalement, j'essaye d'obtenir deux chaînes de cette carte.

  • Première chaîne: questions (dans l'ordre 1 ..10)
  • Deuxième chaîne: réponses (dans le même ordre que la question)

En ce moment, j'ai les éléments suivants:

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

Cela me donne les questions dans une chaîne mais elles ne sont pas en ordre.

n00bstackie
la source

Réponses:

613

Réponse courte

Utilisez a TreeMap. C'est précisément à cela qu'il sert.

Si cette carte vous est transmise et que vous ne pouvez pas déterminer le type, vous pouvez effectuer les opérations suivantes:

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

Cela parcourra la carte dans l'ordre naturel des touches.


Réponse plus longue

Techniquement, vous pouvez utiliser tout ce qui implémente SortedMap, mais à l'exception de rares cas, cela équivaut à TreeMap, tout comme l'utilisation d'une Mapimplémentation équivaut généralement à HashMap.

Pour les cas où vos clés sont d'un type complexe qui n'implémente pas Comparable ou si vous ne voulez pas utiliser l'ordre naturel alors TreeMapet TreeSetavez des constructeurs supplémentaires qui vous permettent de passer 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());

Rappelez-vous lorsque vous utilisez un TreeMapou TreeSetqu'il aura des caractéristiques de performances différentes de HashMapou HashSet. En gros, les opérations qui trouvent ou insèrent un élément passeront de O (1) à O (Log (N)) .

Dans un HashMap, passer de 1000 éléments à 10 000 n'affecte pas vraiment votre temps de recherche d'un élément, mais pour un TreeMaptemps de recherche sera environ 3 fois plus lent (en supposant Log 2 ). Passer de 1000 à 100 000 sera environ 6 fois plus lent pour chaque recherche d'élément.

Jherico
la source
J'essaie d'utiliser Treemap et de trier les clés de chaîne en fonction de la longueur. Je constate que j'obtiens des résultats de récupération incohérents. Apparemment parce que TreeMap considère un résultat compareTo de 0 comme "égal"? Je ne sais pas comment l'utiliser dans ce cas.
Marc
1
compareTo () le résultat de 0 est «égal». Si vous écrivez un comparateur qui trie par longueur de chaîne, vous devez renvoyer une valeur positive ou négative en fonction de la chaîne la plus longue et renvoyer uniquement 0 si les deux chaînes ont la même longueur. si a et b sont des chaînes, vous pouvez le faire comme ceci `retournez a.length () - b.length () '(ou inversez les valeurs si vous voulez qu'elles soient triées dans l'autre sens).
Jherico
Bonjour les gars, s'il veut que la carte soit ordonnée par clés, qui est ici 1,2,3,4, quel est l'ordre d'insertion .... pourquoi ne pas utiliser LinkedHashSet? Nous venons de poser les questions une par une, et elles sont ordonnées selon l'ordre de l'insertion. Est-ce que certains peuvent m'aider avec ça?
Karoly
@Karoly LinkedHashSet fonctionnerait pour récupérer les éléments dans l'ordre dans lequel vous les avez insérés. Ce que l'OP souhaite, c'est récupérer les éléments dans un ordre de tri prédéterminé, indépendamment de l'ordre d'insertion.
David Berry
@ cricket_007, le code montre spécifiquement comment itérer sur les clés d'une carte qui n'est pas déjà triée.
Jherico
137

En supposant que TreeMap n'est pas bon pour vous (et en supposant que vous ne pouvez pas utiliser de génériques):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.
TrayMan
la source
3
Merci! Je devais faire quelque chose comme ça, car mes clés étaient d'un type complexe.
Ross Hambrick
3
Cela ne fera que trier la liste des clés, mais ne triera pas la carte elle-même en fonction des clés. Je cherche également à trier la carte en fonction des clés et je pourrais trouver un moyen. Je vais devoir essayer avec TreeMap, je suppose :)
Crenguta S
55

À l'aide de, TreeMapvous pouvez trier la carte.

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

Utilisez un TreeMap !

AgileJon
la source
31
+1 - Pas assez agile pour battre Jherico, Jon, mais toujours assez bon. 8)
duffymo
Je ne connais pas Java :-( Cela fonctionne à 100%. Beaucoup plus simple que toutes les solutions horribles que j'ai
trouvées
36

Si vous avez déjà une carte et souhaitez la trier sur des clés, utilisez simplement:

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

Un exemple de travail complet:

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
MARYLAND
la source
36

Utilisez simplement TreeMap

new TreeMap<String, String>(unsortMap);

Sachez que le TreeMap est trié selon l'ordre naturel de ses «clés»

Aliti
la source
19

Si vous ne pouvez pas utiliser TreeMap, en Java 8, nous pouvons utiliser la méthode toMap ()Collectors qui prend les paramètres suivants:

  • keymapper : fonction de mappage pour produire des clés
  • valuemapper : fonction de mappage pour produire des valeurs
  • mergeFunction : une fonction de fusion, utilisée pour résoudre les collisions entre les valeurs associées à la même clé
  • mapSupplier : une fonction qui renvoie une nouvelle carte vide dans laquelle les résultats seront insérés.

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

Nous pouvons modifier l'exemple pour utiliser un comparateur personnalisé et trier en fonction des clés comme:

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
la source
ne fonctionne que sur Android avec Api 24 et supérieur.
Tina
10

Utilisation de 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
la source
5

Ce code peut trier une carte de valeurs-clés dans les deux ordres, c'est-à-dire croissant et décroissant.

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

Par exemple:

Map<Integer,Double> recommWarrVals = new HashMap<Integer,Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals,-1);  // Descending order
M. Mashaye
la source
5

Dans Java 8

Pour trier un Map<K, V>par clé, en mettant les clés dans un List<K>:

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

Pour trier une Map<K, V>clé par, en mettant les entrées dans un List<Map.Entry<K, V>>:

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

Dernier point mais non le moindre: pour trier les chaînes de manière sensible aux paramètres régionaux - utilisez une classe Collator (comparateur):

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
la source
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
la source
1

Dans Java 8, vous pouvez également utiliser .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
la source
0

Nous pouvons également trier la clé en utilisant la méthode 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
la source
Il s'agit d'un tri par valeur et non par clé.
raaj
0

Juste au cas où vous ne voudriez pas utiliser un 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;
}

, Vous vouliez en cas également pour trier votre carte sur la base de valuestout changement Map.Entry::getKeydeMap.Entry::getValue

Ankit Sharma
la source
Cela est apparu dans mon journal de révision. Quelqu'un a essayé de corriger certaines erreurs avec les noms de variables (mais a échoué). Donc, je les ai corrigées correctement, cependant, cette réponse est fondamentalement fausse. L'ordre dans lequel vous ajoutez des valeurs à un HashMap n'a pas d'importance. Les cartes n'ont pas d'ordre. stackoverflow.com/questions/10710193/…
aepryus