Introducción: ¿HashMap, amigo o enemigo?
Todo desarrollador Java, tarde o temprano, se encuentra con HashMap. Es, probablemente, la implementación más popular de la interfaz Map en Java. Pero he aquí la paradoja: todos saben cómo poner un elemento en el mapa, pero cuando se trata de recorrerlo (iterar), comienza el caos real. La pregunta "Iterate through a HashMap" en Stack Overflow ha acumulado miles de visitas, y la cantidad de errores en proyectos reales es abrumadora.
En este artículo, analizaremos los 5 errores más peligrosos al recorrer un HashMap en Java, que pueden provocar caídas de la aplicación, pérdida de datos y código ilegible. No solo aprenderá cómo no hacerlo, sino que también obtendrá patrones funcionales para cada caso. ¡Allá vamos!
Error n.º 1: ConcurrentModificationException — "Eliminar sobre la marcha"
¿Cuál es el problema?
El error más clásico. Está iterando sobre un HashMap e intenta eliminar un elemento mediante map.remove(key). En el 90% de los casos, esto provoca una ConcurrentModificationException.
// ❌ PELIGROSO: ¡lanzará una excepción!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! }}¿Por qué sucede esto?
El iterador interno de HashMap mantiene un contador de modificaciones (modCount). Cuando elimina un elemento directamente a través de la colección, el contador cambia, pero el iterador no lo sabe. En la siguiente iteración, verifica: "Oye, ¡el contador no coincide! Lanzamos la excepción".
¿Cómo solucionarlo?
Utilice un Iterator con su método remove():
// ✅ CORRECTO: usamos 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(); // ¡Eliminación segura! }}Con Java 8, apareció una forma aún más elegante: removeIf():
// ✅ AÚN MEJOR: removeIf (Java 8+)scores.entrySet().removeIf(entry -> entry.getValue() < 60);Error n.º 2: Recorrido lento con keySet() y get()
¿Cuál es el problema?
Muchos principiantes hacen esto:
// ❌ LENTO: keySet() + get() — búsqueda doblefor (String key : map.keySet()) { System.out.println("Key: " + key + ", Value: " + map.get(key));}A primera vista, funciona. Pero el problema es que map.get(key) internamente vuelve a buscar el hash y recorre el bucket. En cada iteración del bucle, realiza dos llamadas al HashMap en lugar de una. Para mapas pequeños es imperceptible, pero con cientos de miles de registros, supone una pérdida de rendimiento de hasta el 30-40%.
¿Cómo solucionarlo?
Utilice siempre entrySet() — devuelve pares "clave-valor" en una sola pasada:
// ✅ RÁPIDO: entrySet() — una sola pasadafor (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());}O use forEach con Java 8:
// ✅ CONCISO: forEach (Java 8+)map.forEach((key, value) -> { System.out.println("Key: " + key + ", Value: " + value);});Error n.º 3: Confusión con el orden de los elementos
¿Cuál es el problema?
Ha escrito código que recorre un HashMap y espera que los elementos estén en el orden de inserción. Pero obtiene una secuencia completamente diferente. Esto provoca errores que