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;
}
}

Buen artículo.
Comentario por Ale — 15/10/2007 @ 13:22
Gracias por el articulo
Comentario por Tomás — 16/01/2009 @ 11:16
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
que bueno es joshua bloch………;-)
Comentario por A — 04/03/2009 @ 18:04