Ignorante

11/10/2007

Cálculo correcto del hashCode en Java

Archivado en: Java,Programación — ignorante @ 11:14

Cuando definimos un objeto y redefinimos el método #equals debemos redefinir el método #hashCode

Definir mal el equals es fatal, pero quiero hablar del método #hashCode. Este método devuelve un número con una propiedad fundamental, si dos objetos son iguales (según #equals) el valor retornado por sus respectivos hashCode debe ser igual. Básicamente esta expresión debe ser siempre verdadera:
(!o1.equals(o2)) || (o1.hashCode() == o2.hashCode())
Esto implica que si dos objetos no son iguales (según #equals) pueden tener o no el mismo valor de hashCode.

Implementar mal el hashCode puede pasar inadvertido pero pasa a ser fatal si se usan esos objetos como claves en un HashMap o si se almacenan en un HashSet.

El problema está en que cuando metemos uno de estos objetos en una de esas colecciones se los almacena en diferentes sectores (llamados buckets) según su valor de hashCode. Luego cuando los queremos recuperar pasando como referencia otra instancia que es igual como su valor de hashCode no coincide lo busca en otro sector y no lo encuentra.

Implementar el hashCode de cualquier manera que cumpla la condición mencionada antes tampoco es suficiente, porque si todos los objetos tienen el mismo valor de hashCode van a ir a parar al mismo sector y el HashSet o el HashMap tendrá la misma estructura y tiempos de respuesta de una lista.

Una forma de quedarnos tranquilos de que el valor de hashCode cumple las propiedades y no transforma los HashMaps en listas es haciendo así el cálculo:

public static int genericHash(int... fieldHashes) {
int result = 17;
for (int hash : fieldHashes) {
result = 37 * result + hash;
}
return result;
}

Y un ejemplo de su uso en un objeto:
public class Persona {
private String tipoDocumento;
private int numeroDocumento;
private int hashCode = 0;

@Override
public int hashCode() {
if (this.hashCode == 0) {
this.hashCode = genericHash(this.tipoDocumento.hashCode(),
this.numeroDocumento);
}
return this.hashCode;
}

@Override
public boolean equals(Object obj) {
return obj instanceof Persona
&& ((Persona) obj).tipoDocumento.equals(this.tipoDocumento)
&& ((Persona) obj).numeroDocumento == this.numeroDocumento;
}
}

4 comentarios »

  1. Buen artículo.

    Comentario por Ale — 15/10/2007 @ 13:22

  2. Gracias por el articulo

    Comentario por Tomás — 16/01/2009 @ 11:16

  3. Gracias por el artículo, solo un detalle , la implementación de equals aunque correcto puede dar problemas si se hace comparaciones con clases que hereden de Persona.
    Mas info: http://www.geocities.com/technofundo/tech/java/equalhash.html

    Comentario por Javi — 06/02/2009 @ 16:56

  4. que bueno es joshua bloch………;-)

    Comentario por A — 04/03/2009 @ 18:04


RSS feed para los comentarios de esta entrada. URI para TrackBack.

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s

Tema Rubric. Blog de WordPress.com.

Seguir

Get every new post delivered to your Inbox.

Únete a otros 28 seguidores