Sobes.tech
Middle

Fale sobre o contrato dos métodos hashCode e equals em Java.

sobes.tech IA

Resposta da IA

O contrato dos métodos hashCode e equals em Java define a sua relação e garante um comportamento correto dos objetos ao usar coleções baseadas em tabelas de dispersão (por exemplo, HashSet, HashMap).

Regras principais do contrato:

  1. Reflexividade: Para qualquer valor de referência não nulo x, x.equals(x) deve retornar true.
  2. Simetria: Para quaisquer valores de referência não nulos x e y, x.equals(y) deve retornar true se e somente se y.equals(x) retornar true.
  3. Transitividade: Para quaisquer valores de referência não nulos x, y e z, se x.equals(y) retorna true e y.equals(z) retorna true, então x.equals(z) deve retornar true.
  4. Consistência: Para quaisquer valores de referência não nulos x e y, múltiplas chamadas a x.equals(y) com os mesmos objetos x e y devem sempre retornar o mesmo valor.
  5. Não nulidade: Para qualquer valor de referência não nulo x, x.equals(null) deve retornar false.

E a regra principal que liga hashCode e equals:

  • Se dois objetos são iguais de acordo com o método equals(Object), a chamada ao método hashCode() para cada um desses objetos deve produzir o mesmo inteiro.
  • O inverso não é verdadeiro: se dois objetos têm o mesmo código hash, isso não significa que são iguais.

Valor do contrato:

O não cumprimento do contrato pode levar ao funcionamento incorreto das coleções. Por exemplo, se dois objetos iguais retornam códigos hash diferentes, o HashMap pode colocá-los em diferentes "cestos" (buckets) e não conseguir encontrar um objeto por outro, mesmo que sejam logicamente iguais.

Exemplo de implementação correta:

// Exemplo de classe com equals e hashCode corretos
public final class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        int result = 17; // Número mágico, geralmente primo
        result = 31 * result + x;
        result = 31 * result + y;
        return result;
    }

    // Getters (não obrigatórios para equals/hashCode)
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

Uso:

  • Sempre sobrescreva ambos os métodos (equals e hashCode) juntos.
  • Ao calcular hashCode, use os mesmos campos utilizados em equals.
  • Tente criar "bons" funções de hash que distribuam uniformemente os códigos de hash para minimizar colisões e melhorar o desempenho das coleções hash.
  • Para tipos primitivos, use seus valores. Para objetos, chame hashCode(). Para arrays, use métodos de Arrays.

Na Java 7+ recomenda-se usar Objects.equals(Object a, Object b) e Objects.hash(Object... values) para maior conveniência e correção.

Exemplo com Objects:

// Classe com equals e hashCode usando Objects
import java.util.Objects;

public final class PointWithObjects {
    private final int x;
    private final int y;

    public PointWithObjects(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PointWithObjects that = (PointWithObjects) o;
        return x == that.x && y == that.y; // A lógica de comparação permanece
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y); // Método conveniente para calcular hash
    }
}