161 votes

Tri de HashMap par valeurs

J'ai besoin de trier mes HashMap en fonction des valeurs qui y sont stockées. Le site HashMap contient le nom des contacts stockés dans le téléphone.

J'ai également besoin que les clés soient automatiquement triées dès que je trie les valeurs, ou vous pouvez dire que les clés et les valeurs sont liées ensemble, donc tout changement dans les valeurs doit être reflété dans les clés.

HashMap<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"froyo");
map.put(2,"abby");
map.put(3,"denver");
map.put(4,"frost");
map.put(5,"daisy");

Sortie requise :

2,abby;
5,daisy;
3,denver;
4,frost;
1,froyo;

187voto

Rais Alam Points 3162

Une version générique d'une méthode pour trier un Map ressemble :

private static <K extends Comparable<K>, V extends Comparable<V>> Map<K, V> sort(
        final Map<K, V> unsorted,
        final boolean order) {
    final var list = new LinkedList<>(unsorted.entrySet());

    list.sort((o1, o2) -> order
                          ? o1.getValue().compareTo(o2.getValue()) == 0
                            ? o1.getKey().compareTo(o2.getKey())
                            : o1.getValue().compareTo(o2.getValue())
                          : o2.getValue().compareTo(o1.getValue()) == 0
                            ? o2.getKey().compareTo(o1.getKey())
                            : o2.getValue().compareTo(o1.getValue()));
    return list.stream().collect(
            Collectors.toMap(
                    Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new
            )
    );
}

Le code suivant propose un tri ascendant et descendant par valeur :

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class SortMapByValue
{
    public static final boolean ASC = true;
    public static final boolean DESC = false;

    public static void main(String[] args)
    {

        // Creating dummy unsorted map
        Map<String, Integer> unsortMap = new HashMap<String, Integer>();
        unsortMap.put("B", 55);
        unsortMap.put("A", 80);
        unsortMap.put("D", 20);
        unsortMap.put("C", 70);

        System.out.println("Before sorting......");
        printMap(unsortMap);

        System.out.println("After sorting ascending order......");
        Map<String, Integer> sortedMapAsc = sortByComparator(unsortMap, ASC);
        printMap(sortedMapAsc);

        System.out.println("After sorting descindeng order......");
        Map<String, Integer> sortedMapDesc = sortByComparator(unsortMap, DESC);
        printMap(sortedMapDesc);

    }

    private static Map<String, Integer> sortByComparator(Map<String, Integer> unsortMap, final boolean order)
    {

        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(unsortMap.entrySet());

        // Sorting the list based on values
        Collections.sort(list, new Comparator<Entry<String, Integer>>()
        {
            public int compare(Entry<String, Integer> o1,
                    Entry<String, Integer> o2)
            {
                if (order)
                {
                    return o1.getValue().compareTo(o2.getValue());
                }
                else
                {
                    return o2.getValue().compareTo(o1.getValue());

                }
            }
        });

        // Maintaining insertion order with the help of LinkedList
        Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
        for (Entry<String, Integer> entry : list)
        {
            sortedMap.put(entry.getKey(), entry.getValue());
        }

        return sortedMap;
    }

    public static void printMap(Map<String, Integer> map)
    {
        for (Entry<String, Integer> entry : map.entrySet())
        {
            System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
        }
    }
}

Utilisation des nouvelles fonctionnalités de Java :

 import java.util.*;
 import java.util.Map.Entry;
 import java.util.stream.Collectors;

 public class SortMapByValue

 {
    private static final boolean ASC = true;
    private static final boolean DESC = false;
    public static void main(String[] args)
    {

        // Creating dummy unsorted map
        Map<String, Integer> unsortMap = new HashMap<>();
        unsortMap.put("B", 55);
        unsortMap.put("A", 20);
        unsortMap.put("D", 20);
        unsortMap.put("C", 70);

        System.out.println("Before sorting......");
        printMap(unsortMap);

        System.out.println("After sorting ascending order......");
        Map<String, Integer> sortedMapAsc = sortByValue(unsortMap, ASC);
        printMap(sortedMapAsc);

        System.out.println("After sorting descending order......");
        Map<String, Integer> sortedMapDesc = sortByValue(unsortMap, DESC);
        printMap(sortedMapDesc);
    }

    private static Map<String, Integer> sortByValue(Map<String, Integer> unsortMap, final boolean order)
    {
        List<Entry<String, Integer>> list = new LinkedList<>(unsortMap.entrySet());

        // Sorting the list based on values
        list.sort((o1, o2) -> order ? o1.getValue().compareTo(o2.getValue()) == 0
                ? o1.getKey().compareTo(o2.getKey())
                : o1.getValue().compareTo(o2.getValue()) : o2.getValue().compareTo(o1.getValue()) == 0
                ? o2.getKey().compareTo(o1.getKey())
                : o2.getValue().compareTo(o1.getValue()));
        return list.stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new));

    }

    private static void printMap(Map<String, Integer> map)
    {
        map.forEach((key, value) -> System.out.println("Key : " + key + " Value : " + value));
    }
}

4 votes

Belle réponse, écrite de manière appropriée en utilisant les utilitaires de collecte.

2 votes

J'ai essayé de résoudre mon problème et j'ai constaté qu'il fallait modifier légèrement la logique de votre comparateur, où vous ne vérifiez pas les contrôles de nullité, ce qui devrait être ajouté car les valeurs de la carte peuvent contenir des éléments nuls.

2 votes

Réponse utile et claire.

112voto

Vitalii Fedorenko Points 17469

En Java 8 :

Map<Integer, String> sortedMap = 
     unsortedMap.entrySet().stream()
    .sorted(Entry.comparingByValue())
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue,
                              (e1, e2) -> e1, LinkedHashMap::new));

0 votes

Peut-on utiliser quelque chose comme ça ? Arrays.sort(hm.values().toArray());

1 votes

Ce tri peut être inversé ? les valeurs dont j'ai besoin vont du plus grand au plus petit.

3 votes

@AquariusPower Voir le reversed méthode à docs.oracle.com/javase/8/docs/api/java/util/ Vous pouvez l'appliquer au comparateur retourné par comparingByValue()

99voto

Muse Points 4558

En supposant Java, vous pourriez trier hashmap comme ceci :

public LinkedHashMap<Integer, String> sortHashMapByValues(
        HashMap<Integer, String> passedMap) {
    List<Integer> mapKeys = new ArrayList<>(passedMap.keySet());
    List<String> mapValues = new ArrayList<>(passedMap.values());
    Collections.sort(mapValues);
    Collections.sort(mapKeys);

    LinkedHashMap<Integer, String> sortedMap =
        new LinkedHashMap<>();

    Iterator<String> valueIt = mapValues.iterator();
    while (valueIt.hasNext()) {
        String val = valueIt.next();
        Iterator<Integer> keyIt = mapKeys.iterator();

        while (keyIt.hasNext()) {
            Integer key = keyIt.next();
            String comp1 = passedMap.get(key);
            String comp2 = val;

            if (comp1.equals(comp2)) {
                keyIt.remove();
                sortedMap.put(key, val);
                break;
            }
        }
    }
    return sortedMap;
}

Juste un exemple pour le coup d'envoi. Cette méthode est plus utile car elle trie le HashMap et conserve également les valeurs en double.

0 votes

Je ne pense pas que collections.sort(mapvalues) résoudra le problème.

0 votes

Ce code a arrangé hashmap selon les clés.ce que je voulais était :(2,abby ; 5,daisy ; 3,denver ; 4,frost ; 1,froyo ;)i.e les valeurs sont arrangées selon leurs initiales et le changement se reflète dans les clés...

0 votes

J'ai modifié un peu votre code et cela fonctionne comme souhaité. Merci beaucoup.

36voto

Ola Pola Points 281
map.entrySet().stream()
                .sorted((k1, k2) -> -k1.getValue().compareTo(k2.getValue()))
                .forEach(k -> System.out.println(k.getKey() + ": " + k.getValue()));

2 votes

La solution la plus lisible pour moi et qui fait le travail, par exemple en parcourant un HashMap trié par valeurs

0 votes

Court et direct ; élégant et lisible.

0 votes

Cela imprime dans l'ordre décroissant des valeurs de la carte, comment puis-je le forcer à l'ordre croissant ?

27voto

Jon Skeet Points 692016

Vous ne le faites pas, en fait. A HashMap est fondamentalement non ordonnée. Tout modèle que vous pourrait voir dans la commande devrait pas sur lesquels on peut s'appuyer.

Il existe des cartes triées telles que TreeMap mais ils sont traditionnellement triés par clé plutôt que par valeur. Il est relativement inhabituel de trier par valeur, d'autant plus que plusieurs clés peuvent avoir la même valeur.

Pouvez-vous donner plus de contexte pour ce que vous essayez de faire ? Si vous ne stockez vraiment que des nombres (sous forme de chaînes) pour les clés, peut-être qu'un fichier de type SortedSet comme TreeSet vous conviendrait ?

Vous pourriez aussi stocker deux collections distinctes encapsulées dans une seule classe pour les mettre à jour en même temps ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X