library
beginnerequals() & hashCode() Contract
Understand the contract between equals and hashCode, and what breaks when you violate it.
HashMap finds a key in two steps: hashCode() picks the bucket, then equals() identifies the key inside it. Break either and lookups fail.
hashCode = a filing cabinet drawer number (tells you which drawer to look in). equals = comparing the actual document to confirm it's the right one. Wrong drawer number = you'll never find the document.
Key Concepts
1
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point p)) return false;
return x == p.x && y == p.y;
}2
@Override
public int hashCode() { return Objects.hash(x, y); }3
The contract.
a.equals(b) => a.hashCode() == b.hashCode() required
different hash codes => not equal follows
equal hash codes => may or may not be equal collisions are fine4
Override equals without hashCode and objects vanish.
Set<Point> seen = new HashSet<>();
seen.add(new Point(1, 2));
seen.contains(new Point(1, 2)); // falseequalshashCode
5
The two points are equal, but Object.hashCode gave them different values, so contains looks in the wrong
bucket and never calls equals. You can put entries in and never get them back.
Object.hashCodecontainsequals
6
What equals must satisfy.
reflexive x.equals(x) is true
symmetric x.equals(y) == y.equals(x)
transitive x=y and y=z implies x=z
consistent the answer does not change on its own
null x.equals(null) is always falseequals
7
Symmetry is the one subclasses break. If ColourPoint.equals requires the other object to be a
ColourPoint, then point.equals(colourPoint) and colourPoint.equals(point) disagree — and a collection
holding both behaves differently depending on insertion order.
ColourPoint.equalsColourPointpoint.equals(colourPoint)colourPoint.equals(point)
8
A bad hash code is a performance bug, not a correctness one.
public int hashCode() { return 1; } // legal, and ruinous9
Every key lands in one bucket, so the map degrades to a list: O(n) lookups. Since Java 8 a long bucket
becomes a tree, which softens it to O(log n) — still far off O(1).
10
Use only fields you compare in equals, and avoid mutable ones. A key whose hash changes while it is in
a map is lost.
equals
11
What the interviewer is probing.1. "State the contract between equals and hashCode." Probing: whether you know it precisely.
Stalls: "They should agree." Moves up: equal objects must have equal hash codes; unequal objects
may collide. The reverse is not required, which is why collisions are legal.
12
2. "You add an object to a HashSet, then mutate a field used in equals. What happens?"
Probing: mutable keys. Stalls: "It still finds it." Moves up: its hash changed, so contains
looks in a different bucket and the object is effectively lost — which is why keys should be
immutable.
13
3. "What is wrong with hashCode() returning 1?" Probing: whether you distinguish correctness
from performance. Stalls: "It breaks the map." Moves up: it is legal and correct, but every key
lands in one bucket, so lookups degrade to O(n) — softened to O(log n) since Java 8 when the bucket
becomes a tree.
14
4. "How does subclassing break symmetry?" Probing: the hard part of the contract. Stalls:
"It does not." Moves up: if ColourPoint.equals requires a ColourPoint, then point.equals(cp) and
cp.equals(point) disagree, and collection behaviour depends on insertion order.