Como Iterar um HashMap em Java
Itere entradas de HashMap em Java usando entrySet, keySet, values, forEach e streams.
Um HashMap armazena pares chave-valor, e mais cedo ou mais tarde você precisa percorrê-los — para imprimir um relatório, somar os valores ou filtrar entradas. Java oferece várias formas idiomáticas de fazer isso, cada uma adequada a uma necessidade ligeiramente diferente: você quer as chaves, os valores ou ambos? Este capítulo aborda as abordagens mais comuns — entrySet(), keySet(), values(), forEach e um Iterator para remoção — e explica quando usar cada uma.
Essas técnicas se aplicam a qualquer implementação de Map, incluindo LinkedHashMap e TreeMap, pois todas compartilham a mesma API de iteração.
Percorrendo entradas com entrySet()
Quando você precisa de tanto a chave quanto o valor, entrySet() é a escolha mais eficiente. Ele retorna uma visão de objetos Map.Entry, e uma única passagem fornece cada par sem uma segunda busca:
Map<String, Integer> stock = new HashMap<>();
for (Map.Entry<String, Integer> e : stock.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}Esta é a opção padrão recomendada. Usar keySet() e depois chamar map.get(key) dentro do loop realiza uma busca de hash redundante por elemento; entrySet() evita isso completamente.
Iterando apenas chaves ou valores
Se você só precisa de um lado de cada par, solicite apenas essa visão. keySet() retorna as chaves e values() retorna os valores:
for (String key : stock.keySet()) {
System.out.println("key: " + key);
}
for (int qty : stock.values()) {
System.out.println("qty: " + qty);
}Ambas as visões são sustentadas pelo mapa, portanto refletem seu conteúdo atual sem copiar. Use keySet() quando você genuinamente não precisa dos valores, e values() quando as chaves são irrelevantes.
O método forEach
Desde o Java 8, Map possui um método forEach que recebe um BiConsumer, fornecendo a chave e o valor como parâmetros lambda. É conciso e fácil de ler para efeitos colaterais simples:
stock.forEach((key, value) -> System.out.println(key + "=" + value));Não há break ou continue dentro de um lambda, então para saída antecipada ou fluxo de controle complexo, um laço for clássico ainda é mais claro.
Remoção segura com um Iterator
Modificar um mapa estruturalmente enquanto um laço for-each é executado sobre ele lança ConcurrentModificationException. Para remover entradas durante a travessia, use um Iterator explícito e chame seu método remove():
Iterator<Map.Entry<String, Integer>> it = stock.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getValue() < 10) {
it.remove();
}
}Uma alternativa moderna é stock.entrySet().removeIf(e -> e.getValue() < 10), que expressa o mesmo filtro em uma linha.
| Abordagem | Fornece | Melhor para |
|---|---|---|
entrySet() | chave + valor | padrão; leitura de ambos |
keySet() | apenas chaves | trabalhar com chaves |
values() | apenas valores | totais, varreduras de valores |
forEach | chave + valor (lambda) | efeitos colaterais concisos |
Iterator | chave + valor | remoção durante a travessia |
O que observar na execução:
- O laço
entrySet()lê cada chave e valor em uma única passagem e acumulaTotal stock: 39, somando 12 + 7 + 20. keySet()imprime apenas as chaves (apple,banana,cherry), enquantovalues()imprime apenas os números, mostrando que cada visão expõe um lado do par.- O lambda
forEachproduz as mesmas linhas chave=valor que o laço manual, confirmando que é um equivalente conciso para iteração simples. - Um
LinkedHashMapfoi usado para que a saída mantenha a ordem de inserção — umHashMapsimples não oferece garantia de ordenação, então suas linhas podem aparecer em qualquer sequência. - A chamada
Iterator.remove()removebanana(valor 7, abaixo de 10) e deixa{apple=12, cherry=20}, demonstrando a exclusão segura dentro do laço semConcurrentModificationException.