The equals / hashCode Contract

OOP · lesson 10 of 43 · 4 min read

The rules hash collections rely on, and exactly what breaks when you override only one of the two.

Open this lesson in the learning hub

Key points

  • The rule: objects that are equals must return the same hashCode. Break it and hash collections lose data.
  • HashMap uses the hash to pick a bucket, then equals to search inside it. Wrong bucket, never found.
  • Override both or neither. Doing just one is the classic cause of "my object vanished from the set".
  • equals must be reflexive, symmetric, transitive, consistent, and false for null. instanceof handles null for free.
  • Use Objects.hash(...) over the same fields as equals, and only fields that never change while in a set.
  • The cheapest correct answer is a record. It generates both methods from its components.

Example

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class Main {
    static class Loose {                 // inherits Object.equals: identity only
        final String email;
        Loose(String email) { this.email = email; }
    }

    static final class User {
        private final String email;
        User(String email) { this.email = email; }

        @Override public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof User other)) return false;   // handles null too
            return email.equals(other.email);
        }

        @Override public int hashCode() {
            return Objects.hash(email);                     // same fields as equals
        }
    }

    public static void main(String[] args) {
        Set<Loose> loose = new HashSet<>();
        loose.add(new Loose("a@x.com"));
        loose.add(new Loose("a@x.com"));
        System.out.println("no contract   -> size " + loose.size());

        Set<User> users = new HashSet<>();
        users.add(new User("a@x.com"));
        users.add(new User("a@x.com"));
        System.out.println("with contract -> size " + users.size());
        System.out.println("contains?     -> " + users.contains(new User("a@x.com")));
    }
}

Equal objects must share a hash code, or hash collections quietly swallow them.

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 OOP course, and every lesson in it is listed on the OOP contents page.