1. Comparando objetos en Java

En Java, los objetos se pueden comparar tanto por referencia como por valor.

Comparando referencias

Si dos variables apuntan al mismo objeto en la memoria, entonces las referencias almacenadas en estas variables son iguales. Si compara estas variables usando el operador de igualdad ( ==), obtiene verdadero y ese resultado tiene sentido. Todo es simple aquí.

Código Salida de consola
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Comparando por valor

Pero a menudo puede encontrar situaciones en las que dos variables se refieren a dos objetos distintos que son idénticos. Por ejemplo, dos objetos de cadenas diferentes que contienen el mismo texto.

Para determinar si diferentes objetos son idénticos, utilice el equals()método. Por ejemplo:

Código Salida de consola
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

El equalsmétodo no se limita a la Stringclase. Cada clase lo tiene.

Incluso las clases que escribes por tu cuenta, y he aquí por qué.



2. Objectclase

Todas las clases en Java heredan la Objectclase. A los creadores de Java se les ocurrió este enfoque.

Y si una clase hereda la Objectclase, entonces gana todos los métodos de la Objectclase. Y esta es una de las principales consecuencias de la herencia.

En otras palabras, cada clase tiene los métodos de la Objectclase, incluso si su código no los menciona.

Estos métodos heredados incluyen métodos relacionados con la comparación de objetos. Estos son los métodos equals()y hashCode().

Código En realidad, esto es lo que tendremos:
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
   }
}

En el ejemplo anterior, creamos una Personclase simple con parámetros de nombre y edad, pero no un único método. Pero debido a que todas las clases heredan la Objectclase, la Personclase automáticamente tiene dos métodos:

Método Descripción
boolean equals(Object obj)
Compara el objeto actual y el objeto pasado
int hashCode()
Devuelve el código hash del objeto actual

Resulta que absolutamente todos los objetos tienen el equalsmétodo, y los objetos de diferentes tipos se pueden comparar entre sí. Dicho código se compilará y funcionará perfectamente.

Código Salida de consola
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

El equals()método, heredado de la Objectclase, implementa el algoritmo más simple para comparar el objeto actual con los objetos pasados: solo compara las referencias a los objetos.

Obtiene el mismo resultado si solo compara Personvariables en lugar de llamar al equals()método. Ejemplo:

Código Salida de consola
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

Cuando equalsse llama al método a, simplemente compara la referencia almacenada en la avariable con la referencia almacenada en la bvariable.

Sin embargo, la comparación funciona de manera diferente para la Stringclase. ¿Por qué?

Porque la gente que creó la Stringclase escribió su propia implementación del equals()método.

Implementación del equals()método

Ahora escribamos nuestra propia implementación del método equals en la Personclase. Consideraremos 4 casos principales.

Importante:
Independientemente de qué clase anula el equalsmétodo, siempre toma un Objectobjeto como argumento

Escenario 1 : el mismo objeto en el que equalsse llama al método también se pasa al equalsmétodo. Si las referencias del objeto actual y el objeto pasado son iguales, el método debe devolver true. Un objeto es igual a sí mismo.

En código se verá así:

Código Descripción
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

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


Compara referencias

Escenario 2 : nullse pasa al equalsmétodo: no tenemos nada con lo que comparar. El objeto en el que equalsse llama al método definitivamente no es nulo, por lo que debemos regresar falseen este caso.

En código se verá así:

Código Descripción
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

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


Comparar referencias ¿


Es el objeto pasado null?

Escenario 3 : Personse pasa al equalsmétodo una referencia a un objeto que no es a. ¿ Es el Personobjeto igual al no- Personobjeto? Esa es una pregunta para que el desarrollador de la Personclase decida como él o ella quiera.

Pero, por lo general, los objetos deben ser de la misma clase para ser considerados iguales. Por lo tanto, si se pasa algo que no sea un objeto de la Personclase a nuestro método equals, siempre devolveremos false. ¿Cómo se puede comprobar el tipo de un objeto? Así es, usando el instanceofoperador.

Así es como se ve nuestro nuevo código:

Código Descripción
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
}


Comparar referencias ¿


Es el objeto pasado null?


Si el objeto pasado no es unPerson

4. Comparar dos Personobjetos

¿Con qué terminamos? Si hemos llegado al final del método, entonces tenemos una Personreferencia de objeto que no es null. Entonces lo convertimos a Persony comparamos los datos internos relevantes de ambos objetos. Y ese es nuestro cuarto escenario .

Código Descripción
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
}


Comparar referencias ¿


Es el objeto pasado null?


Si el objeto pasado no es un Person


Typecasting

¿Y cómo comparas dos Personobjetos? Son iguales si tienen el mismo nombre ( name) y edad ( age). El código final se verá así:

Código Descripción
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;
}


Comparar referencias ¿


Es el objeto pasado null?


Si el objeto pasado no es un Person


Typecasting

Pero eso no es todo.

Primero, el campo de nombre es un String, por lo que debe comparar el campo de nombre llamando al equalsmétodo.

this.name.equals(person.name)

En segundo lugar, el namecampo puede ser null: en ese caso, no puede llamarlo equals. Necesita un cheque adicional para null:

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

Dicho esto, si el campo de nombre está nullen ambos Personobjetos, los nombres siguen siendo iguales.

El código para el cuarto escenario podría verse así:

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);


Si las edades no son iguales,
inmediatamente return false

Si this.namees igual a null, no tiene sentido comparar usando el equalsmétodo. Aquí el segundo namecampo es igual a null, o no lo es.

Compare los dos campos de nombre usando el equalsmétodo.


5. hashCode()método

Además del equalsmétodo, que pretende realizar una comparación detallada de todos los campos de ambos objetos, existe otro método que puede utilizarse para una comparación imprecisa pero muy rápida: hashCode().

Imagina que estás ordenando alfabéticamente una lista de miles de palabras y necesitas comparar repetidamente pares de palabras. Y las palabras son largas, compuestas de muchas letras. En términos generales, tal comparación llevaría mucho tiempo.

Pero se puede acelerar. Supongamos que tenemos palabras que comienzan con letras diferentes: inmediatamente queda claro que son diferentes. Pero si comienzan con las mismas letras, todavía no podemos decir cuál será el resultado: las palabras pueden resultar iguales o diferentes.

El hashCode()método funciona utilizando un principio similar. Si lo llama en un objeto, devuelve algún número, análogo a la primera letra de una palabra. Este número tiene las siguientes propiedades:

  • Los objetos idénticos siempre tienen el mismo código hash
  • Diferentes objetos pueden tener el mismo código hash, o sus códigos hash pueden ser diferentes
  • Si los objetos tienen códigos hash diferentes, entonces los objetos son definitivamente diferentes

Para hacer esto aún más claro, reformulemos estas propiedades en términos de palabras:

  • Las palabras idénticas siempre tienen las mismas primeras letras.
  • Diferentes palabras pueden tener las mismas primeras letras, o sus primeras letras pueden ser diferentes
  • Si las palabras tienen primeras letras diferentes, entonces las palabras son definitivamente diferentes

La última propiedad se utiliza para acelerar la comparación de objetos:

Primero, se calculan los códigos hash de los dos objetos. Si estos códigos hash son diferentes, entonces los objetos son definitivamente diferentes y no hay necesidad de compararlos más.

Pero si los códigos hash son los mismos, todavía tenemos que comparar los objetos usando el método de igualdad.



6. Contratos en clave

El comportamiento descrito anteriormente debe ser implementado por todas las clases en Java. Durante la compilación, no hay forma de comprobar si los objetos se comparan correctamente.

Los programadores de Java tienen un acuerdo universal de que si escriben su propia implementación del método equals() y, por lo tanto, anulan la implementación estándar (en la Objectclase), también deben escribir su propia implementación del hashCode()método de tal manera que se cumplan las reglas antes mencionadas. satisfecho.

Este arreglo se llama contrato .

Si implementa solo el equals()o solo el hashCode()método en su clase, entonces está violando gravemente el contrato (ha roto el acuerdo). No hagas esto.

Si otros programadores usan su código, es posible que no funcione correctamente. Además, utilizará un código que se basa en el cumplimiento de los contratos anteriores.

¡Importante!

Al buscar un elemento, todas las colecciones de Java primero comparan los códigos hash de los objetos y solo luego realizan una comparación utilizando el equalsmétodo.

Eso significa que si le da a su propia clase un equalsmétodo pero no escribe su propio hashCode()método o lo implementa incorrectamente, es posible que las colecciones no funcionen correctamente con sus objetos.

Por ejemplo, puede agregar un objeto a una lista y luego buscarlo usando el contains()método, pero es posible que la colección no encuentre su objeto.