Introduction : HashMap — ami ou ennemi ?
Chaque développeur Java rencontre tôt ou tard HashMap. C'est sans doute l'implémentation la plus populaire de l'interface Map en Java. Mais voici le paradoxe : tout le monde sait comment ajouter un élément à la map, mais quand il s'agit de la parcourir (itération), le chaos s'installe. La question "Iterate through a HashMap" sur Stack Overflow a recueilli des milliers de vues, et le nombre d'erreurs dans les projets réels est astronomique.
Dans cet article, nous allons analyser les 5 erreurs les plus dangereuses lors du parcours d'un HashMap en Java, qui peuvent entraîner un plantage de l'application, une perte de données et un code illisible. Vous apprendrez non seulement comment ne pas faire, mais vous obtiendrez également des modèles fonctionnels pour chaque cas. C'est parti !
Erreur n°1 : ConcurrentModificationException — "Je supprime en cours de route"
Quel est le problème ?
L'erreur la plus classique. Vous itérez sur un HashMap et essayez de supprimer un élément via map.remove(key). Dans 90% des cas, cela provoque une ConcurrentModificationException.
// ❌ DANGEREUX : provoquera une exception !HashMap<String, Integer> scores = new HashMap<>();scores.put("Alice", 90);scores.put("Bob", 50);scores.put("Charlie", 70);
for (String name : scores.keySet()) { if (scores.get(name) < 60) { scores.remove(name); // ConcurrentModificationException ! }}Pourquoi cela se produit-il ?
L'itérateur interne de HashMap tient un compteur de modifications (modCount). Lorsque vous supprimez un élément directement via la collection, le compteur change, mais l'itérateur n'en est pas informé. À l'itération suivante, il vérifie : "Hé, le compteur ne correspond pas ! On lève une exception".
Comment corriger ?
Utilisez Iterator avec sa méthode remove() :
// ✅ CORRECT : utilisation d'IteratorHashMap<String, Integer> scores = new HashMap<>();scores.put("Alice", 90);scores.put("Bob", 50);scores.put("Charlie", 70);
Iterator<Map.Entry<String, Integer>> iterator = scores.entrySet().iterator();while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); if (entry.getValue() < 60) { iterator.remove(); // Suppression sécurisée ! }}Avec Java 8, une méthode encore plus élégante est apparue — removeIf() :
// ✅ ENCORE MIEUX : removeIf (Java 8+)scores.entrySet().removeIf(entry -> entry.getValue() < 60);Erreur n°2 : Parcours lent via keySet() et get()
Quel est le problème ?
Beaucoup de débutants font ceci :
// ❌ LENT : keySet() + get() — double recherchefor (String key : map.keySet()) { System.out.println("Key: " + key + ", Value: " + map.get(key));}À première vue, tout fonctionne. Mais le problème est que map.get(key) recherche à nouveau le hash et parcourt le bucket en interne. À chaque tour de boucle, vous effectuez deux appels à HashMap au lieu d'un. Pour les petites maps, c'est imperceptible, mais avec des centaines de milliers d'entrées, cela représente une perte de performance allant jusqu'à 30-40%.
Comment corriger ?
Utilisez toujours entrySet() — il retourne les paires "clé-valeur" en un seul passage :
// ✅ RAPIDE : entrySet() — un seul passagefor (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());}Ou utilisez forEach avec Java 8 :
// ✅ CONCIS : forEach (Java 8+)map.forEach((key, value) -> { System.out.println("Key: " + key + ", Value: " + value);});Erreur n°3 : Confusion sur l'ordre des éléments
Quel est le problème ?
Vous avez écrit un code qui parcourt un HashMap et vous attendez à ce que les éléments soient dans l'ordre d'insertion. Mais vous obtenez une séquence complètement différente. Cela conduit à des bugs qui