Percorrendo Map em Java: como iterar sem dor e sem perda de velocidade

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que seu código pode estar lento sem motivo aparente?

\n

Imagine: você escreveu um algoritmo perfeito, passou em todos os testes unitários, mas em produção a aplicação de repente começa a "ficar lenta". Na maioria das vezes, a culpa não é do algoritmo em si, mas de como você percorre as coleções. Isso é especialmente verdade para o Java Map — uma estrutura de dados presente em 9 de cada 10 projetos corporativos.

\n

A pergunta "How do I efficiently iterate over each entry in a Java Map?" no Stack Overflow acumulou dezenas de milhares de visualizações não por acaso. Muitos desenvolvedores, mesmo com 2-3 anos de experiência, usam abordagens não ideais. Vamos entender como percorrer um Map corretamente e por que um método pode ser 10 vezes mais rápido que outro.

\n\n

Anatomia do Java Map: o que há dentro dele?

\n

Antes de falar sobre iteração, vamos relembrar a estrutura. HashMap é uma tabela hash com buckets. TreeMap é uma árvore rubro-negra. LinkedHashMap é um híbrido com uma lista duplamente encadeada. Cada uma dessas estruturas armazena pares key-value, mas as formas de percorrê-las diferem em velocidade e consumo de memória.

\n

As principais coleções de visualização (view collections) que o Map fornece:

\n
    \n
  • keySet() — conjunto de chaves
  • \n
  • values() — coleção de valores
  • \n
  • entrySet() — conjunto de pares (chave-valor)
  • \n
\n

É através dessas três "janelas" que vamos visualizar os dados.

\n\n

Método nº 1: entrySet clássico com for-each (o mais popular)

\n

Este é um padrão básico, mas muito eficiente. Você obtém acesso tanto à chave quanto ao valor em uma única iteração.

\n
Map<String, Integer> map = new HashMap<>();\n// ... preenchimento do map\nfor (Map.Entry<String, Integer> entry : map.entrySet()) {\n    String key = entry.getKey();\n    Integer value = entry.getValue();\n    // sua lógica\n}
\n

Por que é eficiente? Porque entrySet() retorna uma view (visualização), não uma cópia dos dados. O iterador percorre os nós internos (Node) do HashMap, e cada chamada a next() já fornece tanto a chave quanto o valor. Não há busca adicional por hash.

\n

Quando usar: sempre que precisar de ambos os componentes do par. É o padrão de facto na comunidade Java.

\n\n

Método nº 2: keySet + get (antipadrão a ser evitado)

\n

Muitos iniciantes escrevem assim:

\n
for (String key : map.keySet()) {\n    Integer value = map.get(key);\n    // lógica\n}
\n

À primeira vista, o código é legível. Mas internamente, HashMap.get() recalcula o hash da chave e percorre a cadeia de colisões a cada vez. Para 100 elementos, isso é imperceptível, mas para 100.000, já é uma degradação séria. No pior caso (muitas colisões), a complexidade passa de O(n) para O(n²).

\n

Conclusão: se você precisa da chave e do valor — use entrySet(). Se precisar apenas das chaves — keySet() é ideal.

\n\n

Método nº 3: Iterador com remoção explícita (para mutações durante a iteração)

\n

Às vezes, é necessário remover elementos do Map durante a iteração. O loop for-each lançará uma ConcurrentModificationException. É aqui que o iterador entra em ação:

\n
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();\nwhile (iterator.hasNext()) {\n    Map.Entry<String, Integer> entry = iterator.next();\n    if (entry.getValue() < 0) {\n        iterator.remove(); // remoção segura\n    }\n}
\n

Este método é especialmente útil quando você filtra dados "em tempo real". O iterador funciona em O(1) para cada remoção no HashMap, pois ele conhece o nó atual.

\n\n

Método nº 4: forEach com lambda (Java 8+)

\n

A partir do Java 8, o Map possui seu próprio método forEach:

\n
map.forEach((key, value) -> {\n    System.out.println(key + " -> " + value);\n});
\n

Por baixo dos panos

Blogs

Book Recommendations