AOWS

Just another adrian’s weblog

Cuidado al sobreescribir el método hashCode()

with 6 comments

Modificar los métodos equals y hashCode de nuestros objetos no es algo trivial, y puede traer efectos secundarios no muy buenos para nuestra aplicación.

Para qué sirve el código hash

Por defecto, hashCode devuelve un entero diferente para cada objeto basándose en la dirección del mismo (aunque esto depende de la implementación). Su uso principal es la optimización de las colecciones basadas en hashtables, como pueden ser HashMap o HashSet.

De modo gráfico, imaginemos una colección como una serie de cajas. En la caja 1 guardaremos los objetos que tengan hashcode 1, en la caja 2 los que tengan un hashcode 2… y así respectivamente. Cuando busquemos un objeto, sólo tenemos que mirar en la caja donde debería estar (mediante su hashcode).

En esta caja puede haber más objetos; por ejemplo, si sobreescribimos el método hashCode para que devuelva la longitud de un string:

private class CustomObject {
    private String attribute;

    public void setAttribute(String attribute) {
        this.attribute = attribute;
    }

    @Override
    public int hashCode() {
        return this.attribute.length();
    }

}

y tenemos dos objetos, uno con attribute="a" y otro con attribute="b", ambos irán a parar a la caja 1. Por tanto una vez dentro de la caja correspondiente debemos buscar el objeto mediante el método equals.

Esto es más eficiente que recorrer todos los elementos de la colección (siguiendo con el ejemplo anterior, sólo buscaremos en los strings de longitud 1, descartando el resto de elementos nada más empezar) como pasaría en un ArrayList, por ejemplo.

Peligro, ¡cuidado al sobreescribir!

En condiciones normales no deberíamos (ni necesitaremos, seguramente) sobreescribir el método hashCode. El problema viene cuando redefinimos equals (por ejemplo, para ejecutar tests), lo cual implica que también debemos hacer lo mismo con hashCode para mantener la coherencia (dos objetos iguales según equals deben tener el mismo código hash, sino puede ocurrir que por ejemplo tengamos dos instancias idénticas en un conjunto, cuando esto no puede -o no debería- ocurrir).

Dos objetos distintos según equals pueden producir el mismo código hash; por ejemplo:

    @Override
    public int hashCode() {
        return 2;
    }

Este código sería válido pero ineficiente, todos los objetos irían a parar a la misma caja, por lo que las búsquedas posteriores serían secuenciales perdiendo la principal ventaja de usar hashtables.

Otro gran peligro que existe al sobreescribir el método hashCode es el siguiente:

// creamos una colección de tipo HashSet
Set<CustomObject> testSet = new HashSet<CustomObject>();

// creamos un objeto de tipo CustomObject
// y lo añadimos a la colección
CustomObject customObject = new CustomObject();
customObject.setAttribute("attribute");
testSet.add(customObject);

// comprobamos que esté en el conjunto
System.out.println(testSet.contains(customObject)); // true

// modificamos su atributo y comprobamos
// de nuevo que esté en la colección
customObject.setAttribute("another attribute");
System.out.println(testSet.contains(customObject)); // false

Como vemos, al modificar la variable privada de customObject el código hash del objeto cambia (se utiliza en el cómputo), y cuando queremos comprobar si el objeto pertenece a la colección el método contains busca en la caja que no es. El objeto ya no está en la colección.

En definitiva, no tocar

Esto se puede complicar todavía mucho más si ponemos sobre la mesa objetos serializables y variables transient (no debemos utilizar variables de este tipo en el método hashCode, ya que al recuperar el objeto serializado tendrán el valor por defecto).

Este tema es importante y está presente en muchas situaciones, por ejemplo en las entidades de Hibernate (que puede completar los objetos de una colección con valores autogenerados cambiando el hashcode de los mismos y provocando el lío que vimos anteriormente) y los tests.

Lo mejor es, mientras no sea estrictamente necesario, no tocar.

Written by adrian

18 mayo, 2009 a 23:08

Publicado en Uncategorized

Tagged with

6 comentarios

Subscribe to comments with RSS.

  1. Estas de curso java😉

    Jorge

    21 mayo, 2009 at 07:53

  2. Durante el próximo mes va a ser todavía peor, y es que estoy viendo que escribir sobre ello me ayuda bastante… así que hasta que no haga el examen caerán posts casi a diario😛

    Por cierto, qué alegría “verte”, ya hace tiempo🙂

    adrian

    21 mayo, 2009 at 14:12

  3. De que es el curso?? si se puede saber

    Jorge

    22 mayo, 2009 at 10:40

  4. Estoy preparando la certificación de Sun, y tengo el examen dentro de un mes.

    adrian

    22 mayo, 2009 at 21:24

  5. Con que material de estudio estas preparando la certificacion, me podrias recomendar algun libro, tb quiero rendir la certificacion

    Pablo

    16 marzo, 2011 at 13:58

  6. Hola Pablo, la gran parte del temario la estudié por este libro:

    http://www.amazon.co.uk/dp/0071591060

    Eso sí, yo hice el examen hace casi 2 años y ahora no sé si el libro seguirá siendo válido (además ahora las lleva Oracle).

    adrian

    16 marzo, 2011 at 14:19


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: