iberck
11/6/2015 - 4:23 AM

Equals, Hashcode

Equals, Hashcode

Equals

El método equals es utilizado para cambiar el comportamiento default que compara la igualdad de dos objetos, en la práctica se utiliza comunmente para la correcta manipulación de estructuras de objetos. Por poner un ejemplo, se utiliza para tener un comportamiento personalizado en los métodos que involucran operaciones con los objetos de la colección, por ejemplo: List.remove(Object), contains(object), indexOf(object), removeAll(Collection<?>), remove(object), etc.

Equals y hashcode son dos métodos que están estrechamente ligados, por lo tanto como regla general si sobreescribe equals(), debe sobreescribir hashcode().

Valor por default:

La implementación default de equals() en la clase Object compara las direcciones de memoria y solo retorna true si dos referencias de variables apuntan al mismo lugar en memoria, es decir esencialmente son el mismo objeto.

Hashcode

Hashcode es el código hash de los objetos y se utiliza en las estructuras de datos que hacen uso de hashes para posicionar los elementos. Piense que las estructuras de datos que utilizan el hash de los objetos, lo utilizan para saber dónde posicionarlos dentro de ellas como si fueran cajones:

[hash1][hash2][hash3]  
[hash4][hash5][hash6]  
[hash7][hash8][hash9]  

Una vez que se encuentra el cajón al que corresponde un objeto (hashCode()), se utiliza el método equals() para saber si es igual a los objetos que se encuentran dentro del cajón.

A menudo hashcode() debe retornar un número primo para posicionar mejor el objeto dentro de la estructura de datos.

En java, las estructuras de datos que utilizan hashcode() lo indican mediante el nombre de la estructura de datos: HashSet, LinkedHashSet, HashMap, Hashtable, WeakHashMap.

Valor por default:

Dentro de lo práctico, el método hashCode definido en la clase Object retorna distintos enteros para distintos objetos. Esto es típicamente implementado convirtiendo la dirección de memoria del objeto en un entero.

Equals/hasCode en java

equals/hashCode identifica si dos objetos son iguales, se utiliza frecuentemente en las colecciones para ejecutar operaciones que impliquen igualdad de objetos, por ejemplo para eliminar objetos agregados anteriormente a la colección:

Si no implementas equals/hashcode, las instancias con los mismos datos NO SON CONSIDERADAS LAS MISMAS ya que la implementación por defecto de equals solo retorna true si las instancias tienen la misma referencia.

Question sobreescribe equals/hashcode en base a su alias:

questions.add(new Question("q1"));
questions.add(new Question("q2"));
questions.remove(new Question("q1"));

Un Set por naturaleza no puede tener elementos repetidos, para que java sepa si dos elementos están repetidos utiliza en conjunto equals/hashcode:

questions.add(new Question("q1"));
questions.add(new Question("q1")); // NO SERÁ AGREGADO

Equals/hasCode en hibernate

Como regla general se deben sobreescribir equals/hashCode si:

  • La entidad será parte de un Set
  • Si va a desasociar/reasociar la instancia (no es muy común).

Esto es debido a que hibernate en algunos casos utiliza Proxies.

Como regla general para sobreescribir estos métodos en hibernate es no tomar la propiedad id para equals/hascode porque cambiará la identidad del objeto al guardarlo. En lugar de ello se debe tomar una propiedad que sea inmutable por ejemplo un folio, o mezclar varias propiedades de tal forma que el objeto se distinga de otros.

Si no lo haces, las instancias con los mismos datos no serán consideradas iguales ya que la implementación default de equals solo retorna true cuando las referencias de las instancias son iguales.

Equals/hashCode en Grails

Grails no implementa equals/hashCode por ti ya que no hay una solución genérica que aplique para todos los casos.

Se debe implementar equals/hashcode en los domain class tomando en cuenta las mismas reglas que aplican en hibernate (ver tema de arriba)

Groovy implementa la anotación @EqualsAndHashCode:

@EqualsAndHashCode(includes= ['title', 'author'])
class Book {
  String title
  String author
}

http://stackoverflow.com/questions/29683592/should-i-implement-equals-and-hashcode-in-a-domain-class

Equals en queries hibernate/gorm

El método equals() NO es utilizado para comparar objetos en los queries dentro de hibernate, hibernate compara 2 objetos en base a su id. Si utilizara el método equals() tendría que cargar todos los objetos de la bd para hacer la comparación.

http://stackoverflow.com/questions/1868829/hibernate-query-find-equal-objects

HashcodeBuilder/EqualsBuilder

@Override
public boolean equals(Object obj) {
    if (obj instanceof Question == false) {
        return false;
    }
    if (this == obj) {
        return true;
    }
    final Question otherObject = (Question) obj;

    EqualsBuilder builder = new EqualsBuilder();
    builder.append(this.alias, otherObject.alias);
    return builder.isEquals();
}

@Override
public int hashCode() {
    HashCodeBuilder builder = new HashCodeBuilder();
    builder.append(alias);
    return builder.toHashCode();
}