Stage 4: HashMap internals, lesson 10 of 13

HashMap internals 10: equals() and hashCode()

Intermediate3 min read@since 16Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

HashMap depends on two methods of the key:

  • hashCode() chooses the bucket.
  • equals() finds the exact key inside the bucket.

The contract: objects that are equal must have equal hash codes. Equal hash codes don't have to mean equal objects.

Break it and HashMap misbehaves:

  • equals() without hashCode(): two "equal" keys usually get different (identity) hash codes, so they land in different buckets. You get duplicate keys, and get() with an equal-but-different object returns null.
  • hashCode() without equals(): equal keys reach the same bucket, but equals() still compares identity, so you still get duplicates.
  • Correct: override both, using the same fields. Let your IDE generate them, use Objects.hash, or use a record, which does it for you.
HashMap lab

Example

Java
class Point {
    final int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
    @Override public boolean equals(Object o) {
        return o instanceof Point p && p.x == x && p.y == y;
    }
    // no hashCode(): the contract is broken
}

Map<Point, String> labels = new HashMap<>();
labels.put(new Point(1, 2), "start");
System.out.println(labels.get(new Point(1, 2)));   // null (almost always): different bucket

record Pt(int x, int y) {}                          // equals() and hashCode() generated from x and y
Map<Pt, String> ok = new HashMap<>();
ok.put(new Pt(1, 2), "start");
System.out.println(ok.get(new Pt(1, 2)));           // start

Common mistake

Overriding equals() and forgetting hashCode(). The code compiles and passes simple tests, then HashMap and HashSet silently misbehave.

Under the hood

Use only fields that don't change while the object is a key (part 13), and make equals() and hashCode() agree exactly. Objects.hash is convenient but creates a small array on each call; for very hot keys, write 31 * Integer.hashCode(x) + Integer.hashCode(y) or cache the hash in an immutable class, as String does.

Check yourself

Point overrides equals() but not hashCode(). map.put(new Point(1,2), "a"); what does map.get(new Point(1,2)) usually return?

How this connects

Part of HashMap internals, part by part.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.