1. Comparando objetos em Java

Em Java, os objetos podem ser comparados tanto por referência quanto por valor.

Comparando referências

Se duas variáveis ​​apontam para o mesmo objeto na memória, então as referências armazenadas nessas variáveis ​​são iguais. Se você comparar essas variáveis ​​usando o operador de igualdade ( ==), obterá true e esse resultado fará sentido. Tudo é simples aqui.

Código Saída do console
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Comparando por valor

Mas muitas vezes você pode encontrar situações em que duas variáveis ​​se referem a dois objetos distintos que são idênticos. Por exemplo, dois objetos strings diferentes que contêm o mesmo texto.

Para determinar se objetos diferentes são idênticos, use o equals()método. Por exemplo:

Código Saída do console
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

O equalsmétodo não está limitado à Stringclasse. Toda aula tem.

Mesmo aulas que você escreve por conta própria, e aqui está o porquê.



2. Objectclasse

Todas as classes em Java herdam a Objectclasse. Os criadores do Java criaram essa abordagem.

E se uma classe herda a Objectclasse, ela ganha todos os métodos da Objectclasse. E esta é uma das principais consequências da herança.

Em outras palavras, toda classe tem os métodos da Objectclasse, mesmo que seu código não os mencione.

Esses métodos herdados incluem métodos relacionados à comparação de objetos. Estes são os métodos equals()e hashCode().

Código Na realidade, eis o que teremos:
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

No exemplo acima, criamos uma Personclasse simples com os parâmetros name e age, mas não um único método. Mas como todas as classes herdam a Objectclasse, a Personclasse possui automaticamente dois métodos:

Método Descrição
boolean equals(Object obj)
Compara o objeto atual e o objeto passado
int hashCode()
Retorna o hashcode do objeto atual

Acontece que absolutamente todo objeto tem o equalsmétodo e objetos de diferentes tipos podem ser comparados entre si. Esse código irá compilar e funcionar perfeitamente.

Código Saída do console
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3. equals()método

O equals()método, herdado da Objectclasse, implementa o algoritmo mais simples para comparar o objeto atual com os objetos passados: ele apenas compara as referências aos objetos.

Você obtém o mesmo resultado se apenas comparar Personvariáveis ​​em vez de chamar o equals()método. Exemplo:

Código Saída do console
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

Quando o equalsmétodo é chamado a, ele simplesmente compara a referência armazenada na avariável com a referência armazenada na bvariável.

No entanto, a comparação funciona de maneira diferente para a Stringclasse. Por que?

Porque o pessoal que criou a Stringclasse escreveu sua própria implementação do equals()método.

Implementação do equals()método

Agora vamos escrever nossa própria implementação do método equals na Personclasse. Vamos considerar 4 casos principais.

Importante:
Independentemente de qual classe substitui o equalsmétodo, sempre leva um Objectobjeto como argumento

Cenário 1 : o mesmo objeto no qual o equalsmétodo é chamado também é passado para o equalsmétodo. Se as referências do objeto atual e do objeto passado forem iguais, o método deve retornar true. Um objeto é igual a si mesmo.

No código ficará assim:

Código Descrição
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


Comparar referências

Cenário 2 : nullé passado para o equalsmétodo — não temos nada para comparar. O objeto no qual o equalsmétodo é chamado definitivamente não é nulo, então precisamos retornar falseneste caso.

No código ficará assim:

Código Descrição
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


Compare referências


É o objeto passado null?

Cenário 3 : uma referência a um objeto que não é a Personé passada para o equalsmétodo. O Personobjeto é igual ao não- Personobjeto? Essa é uma questão para o desenvolvedor da Personclasse decidir como quiser.

Mas geralmente os objetos devem ser da mesma classe para serem considerados iguais. Portanto, se algo diferente de um objeto da Personclasse for passado para o nosso método equals, sempre retornaremos false. Como você pode verificar o tipo de um objeto? Isso mesmo — usando o instanceofoperador.

Aqui está a aparência do nosso novo código:

Código Descrição
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


Compare referências


É o objeto passado null?


Se o objeto passado não for umPerson

4. Comparando dois Personobjetos

Com o que terminamos? Se chegamos ao fim do método, então temos uma Personreferência de objeto que não é null. Portanto, convertemos em a Persone comparamos os dados internos relevantes de ambos os objetos. E esse é o nosso quarto cenário .

Código Descrição
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


Compare referências


É o objeto passado null?


Se o objeto passado não for um Person


Typecasting

E como você compara dois Personobjetos? São iguais se tiverem o mesmo nome ( name) e idade ( age). O código final ficará assim:

Código Descrição
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


Compare referências


É o objeto passado null?


Se o objeto passado não for um Person


Typecasting

Mas isso não é tudo.

Primeiro, o campo de nome é um String, então você precisa comparar o campo de nome chamando o equalsmétodo.

this.name.equals(person.name)

Em segundo lugar, o namecampo pode ser null: nesse caso, você não pode invocá equals-lo. Você precisa de uma verificação adicional para null:

this.name != null && this.name.equals(person.name)

Dito isso, se o campo de nome estiver nullem ambos Personos objetos, os nomes ainda serão iguais.

O código para o quarto cenário pode ser assim:

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


Se as idades não forem iguais,
imediatamente return false

Se this.namefor igual a null, não adianta comparar pelo equalsmétodo. Aqui, o segundo namecampo é igual a null, ou não é.

Compare os dois campos de nome usando o equalsmétodo.


5. hashCode()método

Além do equalsmétodo, que se destina a realizar uma comparação detalhada de todos os campos de ambos os objetos, existe outro método que pode ser utilizado para uma comparação imprecisa, mas muito rápida: hashCode().

Imagine que você está classificando alfabeticamente uma lista de milhares de palavras e precisa comparar pares de palavras repetidamente. E as palavras são longas, compostas por muitas letras. De um modo geral, essa comparação levaria muito tempo.

Mas pode ser acelerado. Suponha que temos palavras que começam com letras diferentes - fica imediatamente claro que são diferentes. Mas se eles começam com as mesmas letras, ainda não podemos dizer qual será o resultado: as palavras podem ser iguais ou diferentes.

O hashCode()método funciona usando um princípio semelhante. Se você chamá-lo em um objeto, ele retornará algum número — análogo à primeira letra de uma palavra. Este número tem as seguintes propriedades:

  • Objetos idênticos sempre têm o mesmo hashcode
  • Diferentes objetos podem ter o mesmo hashcode ou seus hashcodes podem ser diferentes
  • Se os objetos tiverem hashcodes diferentes, então os objetos são definitivamente diferentes

Para tornar isso ainda mais claro, vamos reformular essas propriedades em termos de palavras:

  • Palavras idênticas sempre têm as mesmas primeiras letras.
  • Palavras diferentes podem ter as mesmas primeiras letras ou suas primeiras letras podem ser diferentes
  • Se as palavras tiverem primeiras letras diferentes, então as palavras são definitivamente diferentes

A última propriedade é usada para acelerar a comparação de objetos:

Primeiro, os hashcodes dos dois objetos são calculados. Se esses hashcodes forem diferentes, os objetos são definitivamente diferentes e não há necessidade de compará-los mais.

Mas se os hashcodes forem os mesmos, ainda teremos que comparar os objetos usando o método equals.



6. Contratos em código

O comportamento descrito acima deve ser implementado por todas as classes em Java. Durante a compilação, não há como verificar se os objetos são comparados corretamente.

Os programadores Java têm um acordo universal de que, se escreverem sua própria implementação do método equals() e, assim, substituir a implementação padrão (na Objectclasse), eles também devem escrever sua própria implementação do hashCode()método de forma que as regras mencionadas sejam satisfeito.

Esse arranjo é chamado de contrato .

Se você implementar apenas o equals()ou apenas o hashCode()método em sua classe, estará violando grosseiramente o contrato (você quebrou o acordo). Não faça isso.

Se outros programadores usarem seu código, ele pode não funcionar corretamente. Além do mais, você usará um código que depende da adesão aos contratos acima.

Importante!

Ao pesquisar um elemento, todas as coleções Java primeiro comparam os hashcodes dos objetos e só então realizam uma comparação usando o equalsmétodo.

Isso significa que, se você fornecer um equalsmétodo para sua própria classe, mas não escrever seu próprio hashCode()método ou implementá-lo incorretamente, as coleções podem não funcionar corretamente com seus objetos.

Por exemplo, você pode adicionar um objeto a uma lista e procurá-lo usando o contains()método, mas a coleção pode não localizar seu objeto.