Equals, Hashcode
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 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
/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
Como regla general se deben sobreescribir equals
/hashCode
si:
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.
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
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
@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();
}