Introdução: HashMap — amigo ou inimigo?
Todo desenvolvedor Java, mais cedo ou mais tarde, se depara com HashMap. Esta é, provavelmente, a implementação mais popular da interface Map em Java. Mas eis o paradoxo: todos sabem como colocar um elemento no mapa, mas quando se trata de percorrê-lo (iteração), começa o verdadeiro caos. A pergunta "Iterate through a HashMap" no Stack Overflow acumulou milhares de visualizações, e a quantidade de erros em projetos reais é alarmante.
Neste artigo, analisaremos 5 erros mais perigosos ao percorrer um HashMap em Java, que podem levar à queda da aplicação, perda de dados e código ilegível. Você aprenderá não apenas como fazer errado, mas também receberá padrões funcionais para cada caso. Vamos lá!
Erro nº1: ConcurrentModificationException — "Removendo em plena execução"
Qual é o problema?
O erro mais clássico. Você itera sobre um HashMap e tenta remover um elemento usando map.remove(key). Em 90% dos casos, isso resulta em ConcurrentModificationException.
// ❌ PERIGOSO: causará exceção!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 que isso acontece?
O iterador interno do HashMap mantém um contador de modificações (modCount). Quando você remove um elemento diretamente pela coleção, o contador muda, mas o iterador não sabe disso. Na próxima iteração, ele verifica: "Ei, o contador não corresponde! Vamos lançar a exceção".
Como corrigir?
Use o Iterator com seu método remove():
// ✅ CORRETO: usando 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(); // Remoção segura! }}Com Java 8, surgiu uma forma ainda mais elegante — removeIf():
// ✅ AINDA MELHOR: removeIf (Java 8+)scores.entrySet().removeIf(entry -> entry.getValue() < 60);Erro nº2: Percorrer lentamente com keySet() e get()
Qual é o problema?
Muitos iniciantes fazem assim:
// ❌ LENTO: keySet() + get() — busca duplafor (String key : map.keySet()) { System.out.println("Key: " + key + ", Value: " + map.get(key));}À primeira vista, funciona. Mas o problema é que map.get(key) internamente procura o hash novamente e percorre o bucket. A cada iteração do loop, você faz duas chamadas ao HashMap em vez de uma. Para mapas pequenos, isso é imperceptível, mas com centenas de milhares de registros, é uma perda de desempenho de até 30-40%.
Como corrigir?
Sempre use entrySet() — ele retorna pares "chave-valor" em uma única passagem:
// ✅ RÁPIDO: entrySet() — uma passagemfor (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());}Ou use forEach com Java 8:
// ✅ CONCISO: forEach (Java 8+)map.forEach((key, value) -> { System.out.println("Key: " + key + ", Value: " + value);});Erro nº3: Confusão com a ordem dos elementos
Qual é o problema?
Você escreveu um código que percorre um HashMap e espera que os elementos estejam na ordem de inserção. Mas obtém uma sequência completamente diferente. Isso leva a bugs que