Internamente, o HashMap utiliza uma tabela de hash, que distribui os elementos com base no valor retornado pelos métodos hashCode() das chaves. Isso proporciona alta performance em operações como inserção, busca e remoção — geralmente em tempo constante (O(1)).
Características principais
- Chaves únicas: Cada chave no
HashMapdeve ser única. Valores duplicados são permitidos, mas chaves duplicadas substituem o valor anterior. \ - Aceita null: Permite uma chave
nulle vários valoresnull. \ - Alta performance: Operações de
get(),put()eremove()têm complexidade média deO(1), assumindo uma boa função de hash. Em casos extremos com muitas colisões, a complexidade pode serO(log n)(Java 8+). \ - Sem ordem garantida: Os elementos não são armazenados em ordem. Para ordenação por chave ou ordem de inserção, use
TreeMapouLinkedHashMap. \ - Não é thread-safe: Múltiplas threads podem causar comportamentos inesperados sem sincronização. \
Estrutura interna (visual simplificada)
Um HashMap pode ser visualizado como:
Índice: 0 1 2 3 4 5
↓ ↓ ↓ ↓ ↓ ↓
null [A:1] [B:2] null [C:3] null
- Os pares chave-valor são armazenados em buckets (baldes). \
- A chave é transformada em um índice usando a função
hashCode(). \ - Em caso de colisão (duas chaves com o mesmo índice), os elementos são encadeados (lista ligada) ou reestruturados como uma árvore binária balanceada (Java 8+, quando o número de elementos em um bucket excede 8, definido pela constante
TREEIFY_THRESHOLD). \
Exemplo prático
import java.util.HashMap;
public class ExemploHashMap {
public static void main(String[] args) {
HashMap<String, Integer> estoque = new HashMap<>();
estoque.put("Maçã", 3);
estoque.put("Banana", 5);
estoque.put("Laranja", 2);
System.out.println("Bananas em estoque: " + estoque.get("Banana"));
estoque.remove("Maçã");
for (String fruta : estoque.keySet()) {
System.out.println(fruta + " => " + estoque.get(fruta));
}
}
}
Casos de uso reais
- Contagem de frequência de palavras, produtos ou eventos. \
- Mapeamento de identificadores (como usuários → dados). \
- Tabelas de configuração (chave = nome do parâmetro). \
- Caches simples em memória. \
Exemplo de cache
HashMap<String, Integer> cache = new HashMap<>();
public Integer calcularSeNaoCacheado(String chave) {
return cache.computeIfAbsent(chave, k -> calcularValor(k));
}
private Integer calcularValor(String chave) {
// Simula cálculo caro
return chave.length();
}
Comparações com outras coleções
HashMap vs TreeMap vs LinkedHashMap vs ConcurrentHashMap
Característica
HashMap
LinkedHashMap
TreeMap
ConcurrentHashMap
Ordem
Nenhuma
Ordem de inserção
Ordenado por chave
Nenhuma
Performance
Rápido (O(1))
Levemente mais lento
Mais lento (O(log n))
Rápido, thread-safe
Permite null chave
Sim
Sim
Não
Não
Estrutura interna
Tabela de hash
Tabela de hash + lista
Árvore Red-Black
Tabela de hash segmentada
Concorrência
Não thread-safe
Não thread-safe
Não thread-safe
Thread-safe
Erros comuns
- Não sobrescrever
hashCode()eequals()corretamente: \- Se você usa objetos personalizados como chave, implemente ambos os métodos para garantir o funcionamento correto do
HashMap. Uma funçãohashCode()mal implementada, que gera muitos valores iguais, aumenta colisões e degrada o desempenho.
\
import java.util.Objects; \ class Produto { private int id; - Se você usa objetos personalizados como chave, implemente ambos os métodos para garantir o funcionamento correto do
public Produto(int id) {
this.id = id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Produto)) return false;
Produto p = (Produto) o;
return this.id == p.id;
}
}
```
\
- Modificação durante iteração:
\
- Iterar sobre um
HashMape modificá-lo diretamente (semIterator) lançaConcurrentModificationException.
\
Iterator<String> it = mapa.keySet().iterator(); while (it.hasNext()) { String chave = it.next(); it.remove(); // Correto // mapa.remove(chave); // Errado durante iteração } - Iterar sobre um
\
- Confiar na ordem dos elementos:
\
- Não conte com a ordem de iteração — ela pode mudar a cada execução. \
HashMap em ambiente concorrente
HashMap não é seguro para múltiplas threads. Para evitar problemas:
// Versão sincronizada
Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());
// Alternativa moderna e eficiente
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
// Nota: ConcurrentHashMap não permite chaves ou valores null
Boas práticas
- Defina a capacidade inicial e o fator de carga:
\
- Se o número de elementos for grande, especifique a capacidade inicial e o fator de carga para evitar realocações. O fator de carga (0.75 por padrão) determina quando a tabela de hash será redimensionada (ao atingir 75% da capacidade).
\
HashMap<String, Integer> mapa = new HashMap<>(1000, 0.75f); - Se o número de elementos for grande, especifique a capacidade inicial e o fator de carga para evitar realocações. O fator de carga (0.75 por padrão) determina quando a tabela de hash será redimensionada (ao atingir 75% da capacidade).
\
- Evite objetos mutáveis como chave:
\
- Se a chave mudar após a inserção, ela pode se tornar inacessível. \
- Use tipos genéricos:
\
- Evite erros de compilação e casts desnecessários.
\
Map<String, Integer> mapa = new HashMap<>(); - Evite erros de compilação e casts desnecessários.
\
Operações avançadas
- Iterar por pares (chave e valor):
\
for (Map.Entry<String, Integer> entry : mapa.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } // Ou, usando estilo funcional (Java 8+): mapa.forEach((chave, valor) -> System.out.println(chave + ": " + valor));
\
- Verificar se chave ou valor existe:
\
mapa.containsKey("Banana"); // true mapa.containsValue(5); // true
\
- Atualização condicional:
\
mapa.putIfAbsent("Banana", 0); // Insere apenas se a chave não existir mapa.replace("Banana", 10); // Substitui o valor de uma chave existente mapa.compute("Banana", (k, v) -> v == null ? 1 : v + 1); mapa.merge("Banana", 1, Integer::sum);
\
Conclusão
O HashMap é uma das estruturas mais eficientes para associação rápida de dados a uma chave. Ele oferece excelente desempenho, mas requer atenção com a implementação de hashCode() e equals(), especialmente com chaves personalizadas. Com sua combinação de desempenho e flexibilidade, o HashMap é uma ferramenta indispensável para desenvolvedores Java, desde aplicações simples até sistemas complexos.
Quando usar HashMap:
- Busca e inserção rápidas por chave. \
- Armazenamento de dados sem necessidade de ordem. \
- Mapas com muitos elementos. \
Evite se:
- Precisa garantir ordem (use
LinkedHashMapouTreeMap). \ - Está lidando com concorrência sem sincronização (use
ConcurrentHashMap). \
Referências
- Oracle. Class HashMap (Java SE 8). Disponível em: https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html. \
- Oracle. Class HashMap (Java SE 11). Disponível em: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/HashMap.html. \
- GeeksforGeeks. Internal Working of HashMap in Java. Disponível em: https://www.geeksforgeeks.org/internal-working-of-hashmap-java/. Acesso em: 29 abr. 2025. \
- FreeCodeCamp. How Java HashMaps Work – Internal Mechanics Explained. Disponível em: https://www.freecodecamp.org/news/how-java-hashmaps-work-internal-mechanics-explained/. \
- DigitalOcean. Java HashMap – HashMap in Java. Disponível em: https://www.digitalocean.com/community/tutorials/java-hashmap. \
- Medium. Internal Working of HashMap in Java and Performance Improvement in Java 8. Disponível em: https://medium.com/@yashodhara.chowkar/internal-working-of-hashmap-in-java-and-performance-improvement-in-java-8-a28ee1660cda. \
- Stack Overflow. Internal implementation of java.util.HashMap and HashSet. Disponível em: https://stackoverflow.com/questions/1781868/internal-implementation-of-java-util-hashmap-and-hashset. \
- Wikipedia. Java Collections Framework. Disponível em: https://en.wikipedia.org/wiki/Java_collections_framework. \
