Sobes.tech
Middle

Por que é importante redefinir os métodos equals e hashCode em Java?

sobes.tech IA

Resposta da IA

A sobrescrita de equals e hashCode é importante para o funcionamento correto com objetos em coleções e na comparação.

  1. Simetria: Se a.equals(b) retorna true, então b.equals(a) também deve retornar true.
  2. Reflexividade: Para qualquer objeto não nulo a, a.equals(a) deve retornar true.
  3. Transitividade: Se a.equals(b) e b.equals(c) retornam true, então a.equals(c) também deve retornar true.
  4. Consistência: Múltiplas invocações de a.equals(b) devem retornar de forma consistente true ou false, desde que as informações usadas nas comparações de equals não sejam modificadas.

Para hashCode:

  1. Consistência: Se a informação usada nas comparações de equals no objeto não foi modificada, as múltiplas chamadas a hashCode no objeto devem retornar o mesmo valor inteiro.
  2. Ligação com equals: Se dois objetos são iguais de acordo com o método equals, então a chamada a hashCode em cada um dos objetos não iguais deve retornar o mesmo valor inteiro.
  3. Otimização: Não é necessário que, se dois objetos não forem iguais de acordo com equals, a chamada a hashCode em cada um deles retorne resultados inteiros diferentes. No entanto, gerar códigos hash diferentes para objetos não iguais pode melhorar o desempenho das tabelas hash.

O não cumprimento destas regras leva a um comportamento imprevisível ao usar coleções como HashSet, HashMap, ArrayList e outras, que dependem de uma implementação correta desses métodos para determinar a igualdade dos objetos e a sua colocação.

Por exemplo:

// Classe de exemplo sem `equals` e `hashCode` corretos
class PersonBroken {
    private String name;
    private int age;

    public PersonBroken(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // getters omitidos por brevidade
}

// Classe de exemplo com `equals` e `hashCode` corretos
class PersonCorrect {
    private String name;
    private int age;

    public PersonCorrect(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PersonCorrect that = (PersonCorrect) o;
        return age == that.age &&
               Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    // getters omitidos por brevidade
}

// Uso em coleção
List<PersonBroken> brokenList = new ArrayList<>();
brokenList.add(new PersonBroken("Alice", 30));
System.out.println(brokenList.contains(new PersonBroken("Alice", 30))); // Provavelmente false

List<PersonCorrect> correctList = new ArrayList<>();
correctList.add(new PersonCorrect("Alice", 30));
System.out.println(correctList.contains(new PersonCorrect("Alice", 30))); // Provavelmente true

Set<PersonBroken> brokenSet = new HashSet<>();
brokenSet.add(new PersonBroken("Bob", 25));
brokenSet.add(new PersonBroken("Bob", 25));
System.out.println(brokenSet.size()); // Provavelmente 2

Set<PersonCorrect> correctSet = new HashSet<>();
correctSet.add(new PersonCorrect("Bob", 25));
correctSet.add(new PersonCorrect("Bob", 25));
System.out.println(correctSet.size()); // Provavelmente 1

No exemplo com PersonBroken, objetos com os mesmos valores de campos são considerados diferentes nas coleções, pois usam as implementações padrão de equals (compara referências) e hashCode (gera pelo endereço do objeto). No exemplo com PersonCorrect, os métodos sobrescritos permitem às coleções determinar corretamente a equivalência de objetos com base no seu conteúdo.