Comparación de objetos Java con equals () y hashcode ()

En este Java Challenger aprenderá equals()y hashcode()se combinan para hacer comparaciones de objetos eficiente y fácil en los programas Java. En pocas palabras, estos métodos funcionan juntos para verificar si dos objetos tienen los mismos valores.  

Sin equals()y hashcode()tendríamos que " if" crear comparaciones muy grandes , comparando todos los campos de un objeto. Esto haría que el código fuera realmente confuso y difícil de leer. Juntos, estos dos métodos nos ayudan a crear un código más flexible y cohesivo.

Obtén el código fuente de Java Challengers.

Anulación de equals () y hashcode () en Java

La anulación de método es una técnica en la que el comportamiento de la clase o interfaz principal se escribe de nuevo (se anula) en la subclase para aprovechar el polimorfismo. Todos Objecten Java incluyen un método equals()y un hashcode()método, pero deben anularse para que funcionen correctamente.

Para comprender cómo funciona la anulación con equals()y   hashcode(), podemos estudiar su implementación en las clases centrales de Java. A continuación se muestra el equals()método de la Objectclase. El método comprueba si la instancia actual es la misma que la pasada anteriormente Object.

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

Cuando el hashcode()método no se anula, Objectse invocará el método predeterminado de la clase. Este es un método nativo , lo que significa que se ejecutará en otro lenguaje como C y devolverá algún código con respecto a la dirección de memoria del objeto. (No es tan importante saber exactamente cómo funciona este método a menos que esté escribiendo código JDK).

 @HotSpotIntrinsicCandidate public native int hashCode(); 

Cuando los métodos equals()y hashcode()no se anulan, verá los métodos anteriores invocados en su lugar. En este caso, los métodos no cumplen el propósito real de equals()y hashcode(), que es comprobar si dos o más objetos tienen los mismos valores.

Como regla, cuando anula equals()también debe anular hashcode().

Comparar objetos con iguales ()

Usamos el equals()método para comparar objetos en Java. Para determinar si dos objetos son iguales, equals()compara los valores de los atributos de los objetos:

 public class EqualsAndHashCodeExample { public static void main(String... equalsExplanation) { System.out.println(new Simpson("Homer", 35, 120) .equals(new Simpson("Homer",35,120))); System.out.println(new Simpson("Bart", 10, 120) .equals(new Simpson("El Barto", 10, 45))); System.out.println(new Simpson("Lisa", 54, 60) .equals(new Object())); } static class Simpson { private String name; private int age; private int weight; public Simpson(String name, int age, int weight) { this.name = name; this.age = age; this.weight = weight; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Simpson simpson = (Simpson) o; return age == simpson.age && weight == simpson.weight && name.equals(simpson.name); } } } 

En la primera comparación, equals()compara la instancia del objeto actual con el objeto que se ha pasado. Si los dos objetos tienen los mismos valores, equals()volverá true.

En la segunda comparación, equals()comprueba si el objeto pasado es nulo o si está escrito como una clase diferente. Si es una clase diferente, los objetos no son iguales.

Finalmente, equals()compara los campos de los objetos. Si dos objetos tienen los mismos valores de campo, entonces los objetos son iguales.

Analizar comparaciones de objetos

Ahora, veamos los resultados de estas comparaciones en nuestro main()método. Primero, comparamos dos Simpsonobjetos:

 System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120))); 

Los objetos aquí son idénticos, por lo que el resultado será true.

A continuación, volvemos a comparar dos Simpsonobjetos:

 System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45))); 

Los objetos aquí son casi idénticos pero sus nombres son diferentes: Bart y El Barto. Por tanto, el resultado será false.

Finalmente, comparemos un Simpsonobjeto y una instancia de la clase Object:

 System.out.println(new Simpson("Lisa", 54, 60).equals(new Object())); 

En este caso, el resultado será falseporque los tipos de clases son diferentes.

es igual a () versus ==

A primera vista, puede parecer que el ==operador y el equals()método hacen lo mismo, pero en realidad funcionan de manera diferente. El ==operador compara si dos referencias de objeto apuntan al mismo objeto. Por ejemplo:

 System.out.println(homer == homer2); 

En la primera comparación, instanciamos dos instancias diferentes Simpsonusando el newoperador. Debido a esto, las variables homery homer2apuntarán a diferentes Objectreferencias en el montón de memoria. Así que tendremos falsecomo resultado.

System.out.println(homer.equals(homer2)); 

En la segunda comparación, anulamos el equals()método. En este caso, solo se compararán los nombres. Debido a que el nombre de ambos Simpsonobjetos es "Homer", el resultado será true.

Identificar objetos de forma única con código hash ()

Usamos el hashcode()método para optimizar el rendimiento al comparar objetos. La ejecución   hashcode()devuelve un ID único para cada objeto en su programa, lo que facilita la tarea de comparar todo el estado del objeto.

Si el código hash de un objeto no es el mismo que el código hash de otro objeto, no hay razón para ejecutar el equals()método: solo sabe que los dos objetos no son iguales. Por otro lado, si el código hash es el mismo, debe ejecutar el equals()método para determinar si los valores y los campos son los mismos.

Aquí hay un ejemplo práctico con hashcode().

 public class HashcodeConcept { public static void main(String... hashcodeExample) { Simpson homer = new Simpson(1, "Homer"); Simpson bart = new Simpson(2, "Homer"); boolean isHashcodeEquals = homer.hashCode() == bart.hashCode(); if (isHashcodeEquals) { System.out.println("Should compare with equals method too."); } else { System.out.println("Should not compare with equals method because " + "the id is different, that means the objects are not equals for sure."); } } static class Simpson { int id; String name; public Simpson(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o)  if (this == o) return true; if (o == null  @Override public int hashCode() { return id; } } } 

Un hashcode()que siempre devuelve el mismo valor es válido pero no muy efectivo. En este caso, la comparación siempre volverá true, por lo que el equals()método siempre se ejecutará. En este caso, no hay mejora de rendimiento.  

Usando equals () y hashcode () con colecciones

La Setinterfaz es responsable de garantizar que no se inserten elementos duplicados en una Setsubclase. Las siguientes son algunas de las clases que implementan la Setinterfaz:

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

Solo se pueden insertar elementos únicos en a Set, por lo que si desea agregar un elemento a la HashSetclase (por ejemplo), primero debe usar los métodos equals()y hashcode()para verificar que el elemento es único. Si los métodos equals()y hashcode()no se anulan en este caso, corre el riesgo de insertar elementos duplicados en el código.

En el siguiente código, usamos el addmétodo para agregar un nuevo elemento a un HashSetobjeto. Antes de agregar el nuevo elemento, HashSetverifica si el elemento ya existe en la colección dada:

 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; 

Si el objeto es el mismo, no se insertará el nuevo elemento.

Colecciones hash

Setno es la única colección que utiliza equals()y hashcode(). HashMap, Hashtable y LinkedHashMap también requieren estos métodos. Como regla general, si ve una colección que tiene el prefijo "Hash", puede estar seguro de que requiere anular los métodos hashcode()y equals()para que sus características funcionen correctamente.  

Directrices para utilizar equals () y hashcode ()

Solo debe ejecutar un equals()método para objetos que tengan el mismo ID de código hash único. Usted debe no ejecuta equals()cuando el ID de código hash es diferente.

Tabla 1. Comparaciones de códigos hash

Si la hashcode()comparación ... Luego …
devuelve verdadero ejecutar equals()
devuelve falso no ejecutar equals()

This principle is mainly used in Set or Hash collections for performance reasons.

Rules for object comparison

When a hashcode() comparison returns false, the equals() method must also return false. If the hashcode is different, then the objects are definitely not equal.

Table 2. Object comparison with hashcode()

When the hashcode comparison returns ... The equals() method should return ...
true true or false
false false

When the equals() method returns true, it means that the objects are equal in all values and attributes. In this case,  the hashcode comparison must be true as well.

Table 3. Object comparison with equals()

When the equals() method returns ... The hashcode() method should return ...
true true
false true or false

Take the equals() and hashcode() challenge!

It’s time to test your skills with the equals() and hashcode() methods.  Your goal in this challenge is to figure out the output of the two equals() method comparisons and guess the size of the Set collection.

To start, study the following code carefully:

 public class EqualsHashCodeChallenge { public static void main(String... doYourBest) { System.out.println(new Simpson("Bart").equals(new Simpson("Bart"))); Simpson overriddenHomer = new Simpson("Homer") { public int hashCode() { return (43 + 777) + 1; } }; System.out.println(new Simpson("Homer").equals(overriddenHomer)); Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge"))); set.add(new Simpson("Homer")); set.add(overriddenHomer); System.out.println(set.size()); } static class Simpson { String name; Simpson(String name) { this.name = name; } @Override public boolean equals(Object obj) { Simpson otherSimpson = (Simpson) obj; return this.name.equals(otherSimpson.name) && this.hashCode() == otherSimpson.hashCode(); } @Override public int hashCode() { return (43 + 777); } } } 

Remember, analyze the code first, guess the result, and then run the code. Your goal is to improve your skill with code analysis and absorb core Java concepts to make your code more powerful. Choose your answer before checking the correct answer below.

 A) true true 4 B) true false 3 C) true false 2 D) false true 3 

What just happened? Understanding equals() and hashcode()

In the first equals() method comparison, the result is true because the state of the object is exactly the same and the hashcode() method returns the same value for both objects.

In the second equals() method comparison, the hashcode() method is being overridden for the overridenHomer variable. The name is “Homer” for both Simpson objects, but the hashcode() method returns a different value for overriddenHomer. In this case, the final result from the the equals() method will be false because the method contains a comparison with the hashcode.

You might notice that the size of the collection is set to hold three Simpson objects. Let’s check this in a detailed way.

The first object in the set will be will be inserted normally:

 new Simpson("Homer"); 

The next object will be inserted normally, as well, because it holds a different value from the previous object:

 new Simpson("Marge"); 

Finally,  the following Simpson object has the same value as the first object. In this case the object won’t be inserted:

 set.add(new Simpson("Homer")); 

Como sabemos, el overridenHomerobjeto usa un valor de código hash diferente al de la Simpson(“Homer”)instanciación normal . Por esta razón, este elemento se insertará en la colección:

 overriddenHomer; 

Clave de respuesta

La respuesta a esta retador Java es B . La salida sería:

 true false 3 

¡Desafío de video! Depurar equals () y hashcode ()

La depuración es una de las formas más fáciles de absorber completamente los conceptos de programación y al mismo tiempo mejorar su código. En este video puedes seguir mientras depuro y explico Java equals()y el hashcode()desafío.