Cómo iterar un HashMap en Java
Itera entradas de un HashMap en Java usando entrySet, keySet, values, forEach e iteradores.
Un HashMap almacena pares clave-valor, y tarde o temprano necesitas recorrerlos: para imprimir un informe, sumar los valores o filtrar entradas. Java ofrece varias formas idiomáticas de hacer esto, cada una adecuada a una necesidad ligeramente diferente: ¿quieres las claves, los valores o ambos? Este capítulo cubre los enfoques más comunes: entrySet(), keySet(), values(), forEach y un Iterator con eliminación, y explica cuándo usar cada uno.
Estas técnicas se aplican a cualquier implementación de Map, incluyendo LinkedHashMap y TreeMap, ya que todas comparten la misma API de iteración.
Recorrer entradas con entrySet()
Cuando necesitas tanto la clave como el valor, entrySet() es la opción más eficiente. Devuelve una vista de objetos Map.Entry, y un solo recorrido te proporciona cada par sin una segunda búsqueda:
Map<String, Integer> stock = new HashMap<>();
for (Map.Entry<String, Integer> e : stock.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}Esta es la opción recomendada por defecto. Usar keySet() y luego llamar a map.get(key) dentro del bucle realiza una búsqueda hash redundante por elemento; entrySet() lo evita por completo.
Iterar solo claves o valores
Si solo te importa un lado de cada par, solicita únicamente esa vista. keySet() devuelve las claves y values() devuelve los valores:
for (String key : stock.keySet()) {
System.out.println("key: " + key);
}
for (int qty : stock.values()) {
System.out.println("qty: " + qty);
}Ambas vistas están respaldadas por el mapa, por lo que reflejan su contenido actual sin copiar. Usa keySet() cuando genuinamente no necesites los valores, y values() cuando las claves sean irrelevantes.
El método forEach
Desde Java 8, Map tiene un método forEach que acepta un BiConsumer, dándote la clave y el valor como parámetros de una lambda. Es conciso y se lee bien para efectos secundarios simples:
stock.forEach((key, value) -> System.out.println(key + "=" + value));No existe break ni continue dentro de una lambda, así que para salida anticipada o flujo de control complejo, un bucle for clásico sigue siendo más claro.
Eliminación segura con un Iterator
Modificar un mapa estructuralmente mientras un bucle for-each lo recorre lanza ConcurrentModificationException. Para eliminar entradas durante el recorrido, usa un Iterator explícito y llama a su método remove():
Iterator<Map.Entry<String, Integer>> it = stock.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getValue() < 10) {
it.remove();
}
}Una alternativa moderna es stock.entrySet().removeIf(e -> e.getValue() < 10), que expresa el mismo filtro en una sola línea.
| Enfoque | Te proporciona | Ideal para |
|---|---|---|
entrySet() | clave + valor | opción predeterminada; leer ambos |
keySet() | solo claves | trabajar con claves |
values() | solo valores | totales, exploración de valores |
forEach | clave + valor (lambda) | efectos secundarios concisos |
Iterator | clave + valor | eliminar durante el recorrido |
Lo que se puede observar en la ejecución:
- El bucle
entrySet()lee cada clave y valor en un solo recorrido y acumulaTotal stock: 39, sumando 12 + 7 + 20. keySet()imprime solo las claves (apple,banana,cherry) mientras quevalues()imprime solo los números, mostrando que cada vista expone un lado del par.- La lambda
forEachproduce las mismas líneas clave=valor que el bucle manual, confirmando que es un equivalente conciso para iteraciones simples. - Se usó un
LinkedHashMappara que la salida mantenga el orden de inserción; unHashMapsimple no garantiza ningún orden, por lo que sus líneas podrían aparecer en cualquier secuencia. - La llamada a
Iterator.remove()eliminabanana(valor 7, menor que 10) y deja{apple=12, cherry=20}, demostrando la eliminación segura dentro del bucle sin lanzarConcurrentModificationException.