equals and hashCode

Collections · lesson 5 of 42 · 4 min read

Understand why hash collections silently misbehave when these two methods disagree.

Open this lesson in the learning hub

Key points

  • The contract: equal objects must return equal hash codes. Unequal objects may share one — that is just a collision.
  • Override equals but not hashCode: your object lands in one bucket, is looked up in another. Duplicates appear.
  • Symptoms are quiet: set.size() grows unexpectedly and map.get(key) returns null for a key you added.
  • Use a record. It generates a correct equals, hashCode and toString from its components.
  • Never mutate a field used in hashCode while the object sits in a hash collection — you lose it.

Example

import java.util.*;

public class Main {

    static final class BadPoint {
        final int x, y;
        BadPoint(int x, int y) { this.x = x; this.y = y; }
        @Override public boolean equals(Object o) {
            return o instanceof BadPoint p && p.x == x && p.y == y;
        }
        // hashCode() deliberately NOT overridden
    }

    record Point(int x, int y) { }

    public static void main(String[] args) {
        Set<BadPoint> bad = new HashSet<>();
        bad.add(new BadPoint(1, 2));
        bad.add(new BadPoint(1, 2));
        System.out.println("BadPoint set size : " + bad.size() + "   (expected 1)");
        System.out.println("BadPoint contains : " + bad.contains(new BadPoint(1, 2)));

        Set<Point> good = new HashSet<>();
        good.add(new Point(1, 2));
        good.add(new Point(1, 2));
        System.out.println("Point set size    : " + good.size() + "   (expected 1)");
        System.out.println("Point contains    : " + good.contains(new Point(1, 2)));

        Map<Point, String> map = new HashMap<>();
        map.put(new Point(0, 0), "origin");
        System.out.println("map lookup        : " + map.get(new Point(0, 0)));

        System.out.println("equal objects, equal hashCodes ? "
                + (new Point(1, 2).hashCode() == new Point(1, 2).hashCode()));
    }
}

Override equals, override hashCode. Or just use a record and stop worrying.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Collections course, and every lesson in it is listed on the Collections contents page.