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.

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.


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.

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

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 !


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

36

Utilisez simplement TreeMap

new TreeMap<String, String>(unsortMap);

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


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

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

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

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

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

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

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

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


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.