25 Sep 2006

Equals y HashCode en java

Hablamos de los métodos equals y hashCode en java y el porqué es importante en Java

Para ir abriendo boca, voy a recuperar una vieja entrada en mi anterior blog que, curiosamente, se ha convertido en la entrada más visitada de aquel.
Lo primero es hablar del método equals y qué significa. A diferencia de otros lenguajes, en Java el símbolo de igualdad (== ) en objetos indica "la misma instancia", no que sus valores sean iguales; es por eso que se definió un concepto de "objetos iguales" que no es lo mismo que "el mismo objeto".
Esta diferencia se hace con el método equals.
// Aqui tenemos dos clases String
String uno = new String ("Un texto");
String dos = new String ("Un texto");
if (uno == dos ) {// Esta comparacion siempre devuelve false
  ... Esto no se ejecutará
}
if (uno.equals(dos)) {// Esta comparación si devuelve true
 .... Esto si se ejecutará.
}
El método equals y hashCode están relacionados, de forma que dos objetos que sean "iguales" tienen el mismo "código hash":
if (objeto_1.equal (objeto_2))
  System.out.println ("Esto devuelve true: " + obj1.hashCode () == obj2.hashCode ());
Por defecto, el objeto padre Object tiene implementado el método equals como una "igualdad de instancia", por lo que si decidimos no sobrescribirlo, equals y == tienen el mismo resultado.
Quizás parece confuso para los más novatos, pero lo que intento decir es que para cada caso, el método equals deberá de ser diferente. Vamos a ver un ejemplo concreto.

class Punto {
  int x;
  int y;

// el metodo equals seria :
  public boolean equals (Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj instanceof Punto) {
      Punto otro = (Punto)obj;
      return ((x==otro.x) && (y==otro.y))
    } else {
      return false;
    }}}

En este caso, dos puntos son iguales si sus coordenadas son las mismas.

Una vez tenemos claro este concepto del método equals, hay que añadir hashCode. Este método es especialmente importante si la clase se va a emplear en las colecciones. Para ello se incluye en la documentación de Oracle una nota :

dos elementos iguales deben de tener el mismo hashCode, siendo esta una propiedad transitiva. Por lo tanto dos objetos con el mismo hashCode no tienen que ser obligatoriamente iguales

Se deduce que cuando se sobrescribe un método equals es necesario (obligatorio) sobrescribir el método HashCode. Para nuestro ejemplo de la clase "Punto" podríamos hacer:

public int hashCode () {
 // Multiplicamos por numeros primos
 int result = 17;
 result = 37 * result + x;
 result = 37 * result + y;
 return result;
}

Ahora ya tenemos nuestra clase preparada para poder usar el equals correctamente.

Personalmente lo que recomiendo usar algunas soluciones automáticas más cómodas para este tipo de tareas, que en la mayoría de los casos es repetitiva, en todos los IDEs modernos suele existir una opción de "crear método Equals y HashCode", el código es auto-generado y suele ser más que suficiente para cubrir nuestras necesidades.

(Entrada original del 2008)