advanced.java
← Tous les concepts
Méthodes d'objet · ch. 2, p. 11

Le contrat equals/hashCode

Des objets égaux doivent avoir des hash codes égaux. Enfreignez cela et l'objet se comporte mal silencieusement dans toute collection à base de hachage — il entre dans une HashMap et n'en ressort jamais.

equals doit être réflexif, symétrique, transitif, cohérent, et faux pour null. hashCode doit s’accorder avec lui : égal implique hash égal. La réciproque n’est pas exigée — les collisions sont légales.

@Override public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Point)) return false;
    Point p = (Point) o;
    return x == p.x && y == p.y;
}

@Override public int hashCode() {
    return Objects.hash(x, y);
}

Le mode d’échec

Set<Point> set = new HashSet<>();
set.add(new Point(1, 2));
set.contains(new Point(1, 2));   // false si hashCode n'a pas été redéfini

La recherche hache vers un compartiment différent et n’atteint jamais l’appel à equals.

La mutabilité aggrave les choses

Muter un champ qui participe au hashCode après insertion échoue l’élément dans le mauvais compartiment — toujours dans l’ensemble, inaccessible par recherche, et impossible à retirer. Les clés de hachage devraient être immuables.

Symétrie et héritage

Utiliser getClass() != o.getClass() est plus strict que instanceof mais conserve la symétrie quand les sous-classes ajoutent de l’état. instanceof brise la symétrie sauf si la sous-classe n’ajoute aucun champ à la comparaison.

3 questions sur ce concept S'entraîner