Trier une Map par valeurs en Java : comment éviter les vieux pièges (et les nouveaux aussi)
\n\n« Je dois trier un HashMap par valeurs ! » — cette question sur Stack Overflow recueille des milliers de vues et des dizaines de réponses. Quoi de plus simple, semble-t-il ? Prenez une Map, triez-la par values()… et vous avez mal à la tête. Parce que trier une Map par valeurs en Java est une tâche où les débutants (et même les développeurs expérimentés) commettent les mêmes erreurs encore et encore.
Dans cet article, nous allons analyser les 5 erreurs les plus fréquentes lors du tri d'un HashMap par valeurs, montrer les bonnes solutions et fournir des modèles de code prêts à l'emploi. C'est parti !
\n\nErreur n°1 : Tenter naïvement de trier le HashMap lui-même
\n\nL'erreur la plus courante est de penser qu'un HashMap peut être trié « sur place ». HashMap ne garantit pas l'ordre des éléments — c'est sa propriété fondamentale. Vous ne pouvez pas simplement appeler sort() sur un HashMap, car il n'a pas cette méthode.
Voici comment NE PAS faire :
\n\n// ❌ ERREUR : Un HashMap ne peut pas être trié directement\nMap<String, Integer> scores = new HashMap<>();\nscores.put("Alice", 95);\nscores.put("Bob", 80);\nscores.put("Charlie", 90);\n\n// Cette méthode n'existe pas !\n// scores.sort(...); // Erreur de compilation\n\nPourquoi cela ne fonctionne-t-il pas ? HashMap utilise le hachage pour stocker les paires clé-valeur. L'ordre est déterminé par les codes de hachage des clés et peut changer lors de l'ajout de nouveaux éléments. Trier une Map par valeurs n'est possible qu'en créant une nouvelle structure de données — par exemple, LinkedHashMap.
Solution correcte : créer un nouveau LinkedHashMap
\n\n// ✅ CORRECT : trier les Entry et les stocker dans un LinkedHashMap\nMap<String, Integer> sortedMap = scores.entrySet()\n .stream()\n .sorted(Map.Entry.comparingByValue())\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n Map.Entry::getValue,\n (e1, e2) -> e1, // résoudre les conflits (si les clés sont dupliquées)\n LinkedHashMap::new // conserver l'ordre d'insertion\n ));\n\nSystem.out.println(sortedMap);\n// {Bob=80, Charlie=90, Alice=95} — trié par ordre croissant !\n\nErreur n°2 : Oublier LinkedHashMap lors de la collecte
\n\nMême les développeurs Java expérimentés écrivent parfois ce code :
\n\n// ⚠️ PRESQUE CORRECT, mais il y a un détail\nMap<String, Integer> result = scores.entrySet()\n .stream()\n .sorted(Map.Entry.comparingByValue())\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n Map.Entry::getValue\n )); // Par défaut, Collectors.toMap() crée un HashMap !\n\nQuel est le piège ? Collectors.toMap() sans spécifier de fournisseur (supplier) crée un HashMap. Or, HashMap ne garantit pas l'ordre ! Votre tri sera effectué, mais le résultat peut sembler non trié. C'est l'une des erreurs Java les plus sournoises lors du tri d'une Map.
Comment corriger
\n\nToujours spécifier explicitement LinkedHashMap::new comme quatrième argument :
// ✅ GARANTI CORRECT\nMap<String, Integer> result = scores.entrySet()\n .stream()\n .sorted(Map.Entry.comparingByValue())\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n Map.Entry::getValue,\n (oldVal, newVal) -> oldVal,\n LinkedHashMap::new // Point crucial !\n ));\n\nErreur n°3 : Trier par valeurs lorsqu'elles ne sont pas Comparable
\n\nSupposons que vous ayez une Map<String, CustomObject>. Vous écrivez :
// ❌ ERREUR : CustomObject n'implémente pas Comparable\nMap<String, CustomObject> map = new HashMap<>();\n// ...\nmap.entrySet().stream()\n .sorted(Map.Entry.comparingByValue()) // Erreur de compilation !\n