DEV Community

João Antônio
João Antônio

Posted on

equals, hashcode, hashmap

O papel de equals() e hashCode() no Java

Esses dois métodos vêm da classe Object, a base de todas as classes em Java.

Eles determinam como o Java compara objetos e como eles são organizados em coleções baseadas em hash.

equals(Object o)
Enter fullscreen mode Exit fullscreen mode

Define se dois objetos são considerados “iguais”.

Por padrão (em Object), equals() compara referências (endereço na memória).

Classes costumam sobrescrever para comparar conteúdo.

class Cpu {
    String process;
    Pessoa(String process) { this.process = process; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; 
        if (!(o instanceof Pessoa)) 
        Pessoa p = (Pessoa) o;
        return process.equals(p.process);
    }
}
Enter fullscreen mode Exit fullscreen mode
hashCode()
Enter fullscreen mode Exit fullscreen mode

Retorna um número inteiro usado pelo algoritmo de hashing de coleções como HashMap e HashSet.

Serve para descobrir rapidamente onde o objeto deve ficar (qual bucket).

Objetos “iguais” segundo equals() devem ter o mesmo hashCode(),
o contrário não é obrigatório (mesmo hash não garante igualdade).

@Override
public int hashCode() {
    return Objects.hash(nome);
}
Enter fullscreen mode Exit fullscreen mode

Como o HashMap realmente usa isso

Vamos entender o fluxo interno simplificado:

  1. Você chama map.put(chave, valor).
  2. O Java calcula hash = chave.hashCode().
  3. Esse hash é usado pra determinar qual “bucket” no array interno vai armazenar a entrada.
  4. Dentro do bucket, se houver colisões (outros elementos com mesmo hash), ele compara com equals() para decidir se atualiza o valor ou adiciona outro nó.
  5. Quando você faz map.get(chave), ele repete o processo:
    • calcula o mesmo hashCode(),
    • vai ao mesmo bucket,
    • percorre e compara com equals() até achar a chave correspondente.

O que acontece quando a chave muda

Se você altera qualquer campo da chave depois de adicioná-la ao mapa, o hashCode() calculado na inserção não corresponde mais ao hash atual.

Resultado:

  • O get() procura no bucket errado, não encontra.
  • O remove() também falha (não acha a chave antiga).
  • A entrada continua existindo dentro de um bucket “órfão”.
  • Isso não é um “vazamento” clássico, mas é um objeto perdido logicamente — ocupa memória, mas não é acessível.

Exemplo problemático

class Product{
    String name;
    int code;

    Product(String name, int code) {
        this.name= name;
        this.code= code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Product)) return false;
        Produto p = (Product) o;
        return code == p.code;
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora veja o uso:

Map<Produto, Double> precos = new HashMap<>();
Produto p = new Produto("Mouse", 101);
precos.put(p, 79.9);


p.codigo = 999;

// Agora o hash mudou e o mapa não encntra mais a chave
System.out.println(precos.get(p)); //  null
System.out.println(precos.size()); //  1 (ainda existe internamente)
Enter fullscreen mode Exit fullscreen mode

O objeto continua dentro do mapa, mas está “preso” em um bucket diferente e inacessível.

O contrato formal (equals / hashCode contract)

A especificação do Java diz:

Se dois objetos são iguais segundo equals(), eles devem retornar o mesmo valor em hashCode().

Se dois objetos têm hashCode() diferentes, eles não podem ser iguais.

Se dois objetos têm o mesmo hashCode(), eles podem ou não ser iguais (colisão).

Portanto:

equals() → true ⇒ hashCode() igual
hashCode() igual ⇒ equals() pode ser true ou false

feito por João Antônio e caso encontre algum erro, adicione aqui um comentário ou me mande um e-mail.

Top comments (0)