JavaRush /Blogue Java /Random-PT /Pausa para café #168. Por que substituir os métodos equal...

Pausa para café #168. Por que substituir os métodos equals e hashcode em Java?

Publicado no grupo Random-PT

Por que substituir os métodos equals e hashcode em Java?

Fonte: Medium Este artigo se concentra em dois métodos intimamente relacionados: equals() e hashcode() . Você aprenderá como eles interagem entre si e como substituí-los corretamente. Pausa para café #168.  Por que substituir os métodos equals e hashcode em Java?  - 1

Por que substituímos o método equals()?

Em Java, não podemos sobrecarregar o comportamento de operadores como == , += , -+ . Eles funcionam de acordo com um determinado processo. Por exemplo, considere a operação do operador == .

Como funciona o operador ==?

Ele verifica se as duas referências comparadas apontam para a mesma instância na memória. O operador == só será avaliado como verdadeiro se as duas referências representarem a mesma instância na memória. Vamos dar uma olhada no código de exemplo:
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
Digamos que em seu programa você criou dois objetos Person em locais diferentes e deseja compará-los.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
Do ponto de vista empresarial, os dois parecem iguais, certo? Mas para a JVM eles não são iguais. Como ambos são criados usando a palavra-chave new , essas instâncias estão localizadas em segmentos de memória diferentes. Portanto, o operador == retornará false . Mas se não podemos substituir o operador == , então como podemos dizer à JVM que queremos que esses dois objetos sejam tratados da mesma forma? É aqui que o método .equals() entra em ação . Você pode substituir equals() para verificar se alguns objetos possuem os mesmos valores para determinados campos, a fim de considerá-los iguais. Você pode escolher quais campos comparar. Se dissermos que dois objetos Person serão iguais apenas se tiverem a mesma idade e o mesmo nome, então o IDE irá gerar algo assim para criar automaticamente equals() :
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
Voltemos ao nosso exemplo anterior.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
Sim, não podemos sobrecarregar o operador == para comparar objetos da maneira que desejamos, mas Java nos oferece outra maneira - o método equals() , que podemos substituir como desejarmos. Tenha em mente que se não fornecermos nossa versão personalizada de .equals() (também conhecida como substituição) em nossa classe, então o .equals() predefinido da classe Object e o operador == se comportarão da mesma forma. O método equals() padrão , herdado de Object , verificará se ambas as instâncias comparadas são iguais na memória!

Por que estamos substituindo o método hashCode()?

Algumas estruturas de dados em Java, como HashSet e HashMap , armazenam seus elementos com base em uma função hash aplicada a esses elementos. A função hash é hashCode() . Se tivermos a opção de substituir o método .equals() , também deveremos ter a opção de substituir o método hashCode() . Há uma razão para isso. Afinal, a implementação padrão de hashCode() , herdada de Object , considera todos os objetos na memória como únicos! Mas vamos voltar a essas estruturas de dados hash. Existe uma regra para essas estruturas de dados. Um HashSet não pode conter valores duplicados e um HashMap não pode conter chaves duplicadas. Um HashSet é implementado usando um HashMap de forma que cada valor do HashSet seja armazenado como uma chave no HashMap . Como funciona o HashMap ? HashMap é um array nativo com vários segmentos. Cada segmento possui uma lista vinculada ( linkedList ). Esta lista vinculada armazena nossas chaves. HashMap encontra o linkedList correto para cada chave usando o método hashCode() e, em seguida, itera por todos os elementos desse linkedList e aplica o método equals() a cada um desses elementos para verificar se esse elemento está contido lá. Chaves duplicadas não são permitidas. Pausa para café #168.  Por que substituir os métodos equals e hashcode em Java?  - 2Quando colocamos algo dentro de um HashMap , a chave é armazenada em uma dessas listas vinculadas. A lista vinculada em que esta chave será armazenada é mostrada pelo resultado do método hashCode() para essa chave. Ou seja, se key1.hashCode() resultar em 4, então essa key1 será armazenada no 4º segmento do array no LinkedList ali existente . Por padrão, o método hashCode() retorna resultados diferentes para cada instância. Se tivermos um equals() padrão que se comporte como == , tratando todas as instâncias na memória como objetos diferentes, então não haverá problema. Como você deve se lembrar, em nosso exemplo anterior dissemos que queremos que as instâncias Person sejam consideradas iguais se suas idades e nomes forem iguais.
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
Agora vamos criar um mapa para armazenar essas instâncias como chaves com uma string específica como par de valores.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Na classe Person , não substituímos o método hashCode , mas temos um método equals substituído . Como o hashCode padrão fornece resultados diferentes para diferentes instâncias Java de person1.hashCode() e person2.hashCode() , há grandes chances de obter resultados diferentes. Nosso mapa pode terminar com diferentes pessoas em diferentes listas vinculadas. Pausa para café #168.  Por que substituir os métodos equals e hashcode em Java?  -3Isso vai contra a lógica do HashMap . Afinal, um HashMap não pode ter várias chaves idênticas! A questão é que o hashCode() padrão herdado da classe Object não é suficiente. Mesmo depois de substituirmos o método equals() da classe Person . É por isso que temos que substituir o método hashCode() depois de substituir o método equals . Agora vamos consertar isso. Precisamos substituir nosso método hashCode() para que ele leve em consideração os mesmos campos de equals() , ou seja, age e name .
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
No método hashCode() usamos um valor simples (você pode usar qualquer outro valor). No entanto, sugere-se a utilização de números primos para criar menos problemas. Vamos tentar armazenar essas chaves em nosso HashMap novamente :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode() e person2.hashCode() serão iguais. Digamos que sejam 0. O HashMap irá para o segmento 0 e nele o LinkedList salvará person1 como uma chave com o valor “1”. No segundo caso, quando o HashMap for novamente ao bucket 0 para armazenar a chave person2 com o valor “2”, ele verá que lá já existe outra chave igual a ela. Dessa forma, ele substituirá a chave anterior. E apenas a chave person2 existirá em nosso HashMap . Foi assim que aprendemos como funciona a regra HashMap , que afirma que você não pode usar várias chaves idênticas! No entanto, lembre-se de que instâncias desiguais podem ter o mesmo código hash e instâncias iguais devem retornar o mesmo código hash.Pausa para café #168.  Por que substituir os métodos equals e hashcode em Java?  - 4
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION