Memra

The equals() & hashCode() contract

Why you must override both, the contract clauses, and the consequence of getting it wrong in a hash container.

Why hash containers need both methods

HashMap, HashSet, and friends find an element in three steps: (1) call hashCode() to pick a bucket, (2) within that bucket compare candidates with equals(). If you override only one of the two methods, those steps disagree and the container loses your objects.

The default Object.equals is identity (==, same reference) and the default Object.hashCode is derived from the object's identity. So two *logically equal* objects you created separately are, by default, unequal and have different hash codes. For a value class (a class whose identity is its contents — a Point, a Money, a SkillCode) you almost always want logical equality, which means overriding both.

### The contract (you must obey all of it)

equals must be reflexive (x.equals(x)), symmetric (x.equals(y)y.equals(x)), transitive, consistent (same result on repeated calls), and x.equals(null) must be false.

The binding rule linking the two methods:

- If a.equals(b) is true, then a.hashCode() == b.hashCode() MUST be true. - The converse is not required: two unequal objects *may* share a hash code (a *collision*) — that is allowed and merely costs a little speed.

So: equal objects must have equal hash codes. Violate this and a HashSet may store two objects you consider equal, or fail to find an object you just inserted.

Worked example. A value class Pair overriding both, using the same fields in each, with Objects.equals (null-safe) and Objects.hash:

class Pair {
    final String key; final int value;
    Pair(String key, int value) { this.key = key; this.value = value; }

    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Pair)) return false;
        Pair p = (Pair) o;
        return value == p.value && Objects.equals(key, p.key);
    }

    @Override public int hashCode() {
        return Objects.hash(key, value);
    }
}

Now new Pair("a", 1).equals(new Pair("a", 1)) is true and both produce the same hashCode(), so a HashSet<Pair> correctly treats them as one element. Note the same fields (key, value) drive *both* methods — that is what keeps them consistent.

NORMAL ~/memra/learn/comp-308/equals-and-hashcode-contract utf-8 LF