Todos los días cuando programamos usamos objetos y en muchas ocasiones necesitamos comparar unos con otros. Para ello en muchas ocasiones usamos los métodos de de Java equals y hashcode. Estos métodos generan muchas dudas entre los desarrolladores a la hora de usarlos.
Vamos a crear una implementación por defecto para la clase Persona que realice un override sobre ambos métodos para entenderlos mejor.
package com.arquitecturajava; public class Persona { private String nombre; private int edad; public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public int getEdad() { return edad; } public void setEdad(int edad) { this.edad = edad; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((nombre == null) ? 0 : nombre.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Persona other = (Persona) obj; if (nombre == null) { if (other.nombre != null) return false; } else if (!nombre.equals(other.nombre)) return false; return true; } }
El método equals es el más sencillo de entender ya que comprueba si los dos objetos son del mismo tipo y si su nombre coincide. En tal caso el resultado será true y en todos los demás false. Para ello el método realiza una serie de comparaciones.
El método hashcode
Este método viene a complementar al método equals y sirve para comparar objetos de una forma más rápida en estructuras Hash ya que únicamente nos devuelve un número entero. Cuando Java compara dos objetos en estructuras de tipo hash (HashMap, HashSet etc) primero invoca al método hashcode y luego el equals. Si los métodos hashcode de cada objeto devuelven diferente hash no seguirá comparando y considerará a los objetos distintos. En el caso en el que ambos objetos compartan el mismo hashcode Java invocará al método equals() y revisará a detalle si se cumple la igualdad. De esta forma las búsquedas quedan simplificadas en estructuras hash.
Manejando colecciones
Muchas veces se nos olvida que la invocación a los métodos equals y hashcode forma parte intrínseca del framework de colecciones. Por ejemplo si construimos dos objetos de tipo Persona y los añadimos a un HashSet, podemos comprobar que la Persona existe utilizando el método contains dentro del conjunto.
package com.arquitecturajava; import java.util.HashSet; public class Principal { public static void main(String[] args) { Persona p1= new Persona(); p1.setNombre("david"); Persona p2= new Persona(); p2.setNombre("miguel"); HashSet<Persona> conjunto= new HashSet<Persona>(); conjunto.add(p1); conjunto.add(p2); System.out.println(conjunto.contains(p1)); } }
Esto nos devolverá true ya que el HashSet contiene este elemento. Ahora bien si nosotros sobreescribimos de forma incorrecta el hashcode con el siguiente código:
@Override public int hashCode() { return (int)(Math.random()*1000); }
Estaremos calculando al azar el hashcode y dos objetos iguales devolverán hashcodes diferentes .El HashSet nos devolverá false cuando invoquemos el método contains aunque sabemos que el elemento existe en el conjunto.
Esto se debe a que Java comparará dos elementos primero por su hashcode y como no coinciden directamente devolverá falso . Por lo tanto si queremos sobreescribir correctamente los métodos equals y hashcode debemos asegurarnos de que cuando dos objetos sean iguales devuelvan el mismo hashcode. Eso sí aunque nos parezca curioso dos objetos diferentes pueden tener mismo hashcode. Esto no está en constradicción con lo anteriormente expuesto.
Hola Cecilio,
muchas gracias por tu aportación.
Tengo unas dudas que te agradecería mucho si pudieses despejarlas.
¿El hashCode se calcula sobre cada atributo de la clase: nombre.hashCode() y también sobre el objeto en su totalidad?
Y otra duda es que me cuesta seguir qué objeto se compara con qué otro. Me explico, con el código:
public boolean equals(Object obj) {
if (this == obj)
return true;
¿De dónde procede el objeto obj y de dónde el objeto this?
Muchas gracias.
El objeto “obj” hace referencia al segundo objeto de la comparación es decir a.equals(b) hace referencia a b y el objeto this hace referencia al objeto a
Un saludo
obj hace referencia al objeto que será pasado por argumento al metodo equals. this hace referencia a la propia clase donde se encuentre.
si 🙂
Gracias por el aporte, muy bien explicado. Saludos cordiales!
de nada 🙂
Excelente me sirvio, cuando estaba mirando el codigo del video jpa.
saludos cordiales!
gracias 🙂
Hola
Tengo una pequeña duda, ¿si estoy utilizando una ConcurrentHashMasp, también estoy obligado a reescribir los metodos hashCode y equals?
Muchas gracias,
un saludo
En tu clase si , sino los sobreescribes correctamente puede que no te encuentre algunos de los elementos del mapa
Muy bien explicado el post!
Te hago una consulta, me han preguntado en una entrevista si una clase tiene que tener si o si sobreescritos los métodos equals y hashcode, o puede estar uno sin el otro(por ejemplo tener el hashcode pero no el equals, o viceversa).
A lo que contesté que se podía tener el equals sin el hashcode(Siempre y cuando no se utilice en una collección de tipo Hash)
¿Es correcto lo que dije, o tienen que estár los dos métodos sin falta?
Muchas gracias.
Saludos.
Me temo que no primero se invoca el hash y luego el equals . De esta manera se acelera la comparación 🙂
No era correcto lo que te dije exactamente dependiendo de la colección se invoca el hash o no 🙂
Hola ,
quería preguntarte que en el caso en el que tuvieras un HashMap<Persona,ArrayList> como utilizarías el método get para acceder al key del hashMap?
Un saludo ,Bogdan.
Normalmente a las clases se accede con el metodo keys que permite recorrerlas , pero con el método get se accede al valor
Hola ,
quería preguntarte que en el caso en el que tuvieras un HashMap<Persona,ArrayList> como utulizarías el método get para acceder al key del hashMap?
Un saludo ,Bogdan.
Buena explicación Cecilio, gracias por compartir tus conocimientos … una consulta al momento de que sobreescribes el metodo hashcode
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((nombre == null) ? 0 : nombre.hashCode());
return result;
}
los valores de prime=31 y result=1 son arbitrarios no?, puedo colocar cualquier valor cierto?
y la variable result almacena el valor hashcode de cada objeto por el atributo nombre
Gracias
Los valores son primos para generar una mejora de la repartición de las claves en los distintos tipos de estructura de datos en donde almacenemos los objetos
Muy buen articulo, bastante facil de enterder gracias a tu forma de explicarlo.
Gracias
de nada 🙂 , me alegro de que te fuera útil.
Hola, estoy introduciéndome en el mundo Java sin ser programador y este artículo ha sido muy esclarecedor. Puesto que son métodos “invisibles” uno que no sabe que otros métodos los utilizan y por tanto es difícil averiguar para que sirven. Gracias de verdad, me has ahorrado un buen dolor de cabeza.
Por otra parte, creo que sería muy instructivo para mi poder ver el código que se escode detrás de los métodos “pre-hechos”(lo siento, no se como llamarlos) . Por ejemplo cual es el código que se esconde detrás de HashSet.add(object). Sabes alguna forma de ver ese código? Gracias.
Te tendrias que bajar los fuentes del JDK https://jdk7.java.net/source.html
[…] artículos relacionados: Java equals y hashcode , this() y super, java fluid […]
Muchas gracias por tus post, son buenos.
gracias 🙂