Ao utilizar HashMap no Java com objetos personalizados como chave, é fundamental sobrescrever os métodos hashCode() e equals(). O mesmo vale para métodos como contains() em coleções como ArrayList. Entenda onde e por que esses métodos são exigidos.
O HashMap implementa uma tabela hash baseada no código hash dos objetos. Cada chave gera um valor inteiro (hashCode) que determina o bucket onde o par chave-valor será armazenado. Durante a inserção ou busca, a coleção primeiro compara os códigos hash; se houver colisão, o método equals é utilizado para verificar se as chaves são logicamente equivalentes. Por isso, ao usar classes próprias como chave, é indispensável que ambas as implemantações estejam alinhadas.
A implementação padrão de hashCode() herdada de Object retorna o endereço de memória do objeto. Consequentemente, dois objetos com o mesmo conteúdo lógico (mesmo estado) terão códigos hash diferentes, fazendo com que HashMap os trate como chaves distintas. O mesmo ocorre com equals() padrão, que também compara referências. Para que objetos "iguais" semanticamente sejam tratados como a mesma chave, precisamos redefinir ambos os métodos.
Exemplo com HashMap
Considere as classes Usuario e Arquivo abaixo. Sem a sobrescrita adequada, a busca por uma chave logicamente igual retornará null.
import java.util.HashMap;
import java.util.Map;
public class ExemploHashMap {
public static void main(String[] args) {
Map<Usuario, Arquivo> mapa = new HashMap<>();
mapa.put(new Usuario("joao", "joao@email.com"), new Arquivo("relatorio.pdf", "conteudo A"));
mapa.put(new Usuario("admin", "admin@empresa.com"), new Arquivo("config.xml", "dados B"));
// Sem hashCode e equals sobrescritos, o retorno é null
Arquivo resultado = mapa.get(new Usuario("admin", "admin@empresa.com"));
System.out.println("Resultado da busca: " + resultado);
}
}
class Usuario {
private String nome;
private String email;
public Usuario(String nome, String email) {
this.nome = nome;
this.email = email;
}
// Getters e setters omitidos para brevidade
@Override
public int hashCode() {
final int PRIMO = 37;
int hash = 1;
hash = PRIMO * hash + (email == null ? 0 : email.hashCode());
hash = PRIMO * hash + (nome == null ? 0 : nome.hashCode());
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Usuario outro = (Usuario) obj;
return java.util.Objects.equals(nome, outro.nome) &&
java.util.Objects.equals(email, outro.email);
}
}
class Arquivo {
private String titulo;
private String conteudo;
public Arquivo(String titulo, String conteudo) {
this.titulo = titulo;
this.conteudo = conteudo;
}
@Override
public String toString() {
return "Arquivo{" + "titulo='" + titulo + '\'' + ", conteudo='" + conteudo + '\'' + '}';
}
@Override
public int hashCode() {
final int PRIMO = 37;
int hash = 1;
hash = PRIMO * hash + (conteudo == null ? 0 : conteudo.hashCode());
hash = PRIMO * hash + (titulo == null ? 0 : titulo.hashCode());
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Arquivo outro = (Arquivo) obj;
return java.util.Objects.equals(titulo, outro.titulo) &&
java.util.Objects.equals(conteudo, outro.conteudo);
}
}
Com a sobrescrita, o método get encontra a chave corretamente, pois os códigos hash coincidem e equals retorna verdadeiro para objetos semanticamente iguais.
Exemplo com ArrayList e contains
O mesmo princípio se aplica a ArrayList.contains(). O método intenramente percorre a lista e chama equals para cada elemento. Sem a sobrescrita, mesmo objetos com conteúdo idêntico são considerados diferentes.
import java.util.ArrayList;
import java.util.List;
public class ExemploArrayList {
public static void main(String[] args) {
List<Arquivo> lista = new ArrayList<>();
lista.add(new Arquivo("contrato.pdf", "cláusulas"));
lista.add(new Arquivo("nota_fiscal.xml", "valores"));
// Sem equals sobrescrito, retorna false
boolean existe = lista.contains(new Arquivo("contrato.pdf", "cláusulas"));
System.out.println("Elemento encontrado? " + existe);
}
}
Após sobrescrever equals (e, por consistência, hashCode), o método contains retorna true para objetos logicamente equivalentes.
Em resumo, sempre que uma classe for usada como chave em estruturas baseadas em hash (HashMap, HashSet) ou quando a igualdade lógica for necessária em coleções (ArrayList.contains, remove, etc.), é obrigatório sobrescrever hashCode() e equals() de forma consistente.