skip to content

hashCode Contract

hashCode must be consistent within a run and equal for equal objects, while unequal objects are allowed to collide. Interviewers check that you know the guarantee runs one way only, and that hashes are not stable across JVM runs.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is the hashCode() contract in Java? State its rules.

level: juniorimportance: must knowfreq 82%

answer

  1. Equal objects -> equal hashCodes (the load-bearing rule)
  2. Consistent within ONE run only
  3. Unequal MAY collide (allowed, just slower)
  4. Same fields as equals, no more no less
  5. Default = per-run identity hash, not address

basics

~20 s

hashCode() returns an int for an object. Two rules matter: if two objects are equal (equals() is true), they must return the same hashCode; and a hashCode must stay the same across repeated calls during one run. Unequal objects may share a code.

solid answer

~40 s

The hashCode() contract has three parts. First, consistency: as long as the object's equals-relevant state is unchanged, repeated hashCode() calls in the same JVM run must return the same int. Second, the equals link: if a.equals(b) is true, then a.hashCode() == b.hashCode() must hold. Third, the collision allowance: if two objects are unequal, their hashCodes are not required to differ — they may collide; producing distinct codes for unequal objects merely improves hash-table performance. Note what the contract does NOT promise: hashCode values are not stable across different JVM runs (the default identity hash is derived per-run), and unequal objects with equal codes are perfectly legal. Hash-based collections (HashMap, HashSet) rely on the equals link being honored, which is why overriding equals without overriding hashCode breaks them.

code

java · 18 lines
java
final class Point {
    private final int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }

    @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;
    }

    // Derived from the SAME fields equals uses -> equal objects get equal codes.
    @Override public int hashCode() {
        return java.util.Objects.hash(x, y);
    }
}

// new Point(1,2).hashCode() == new Point(1,2).hashCode()  // true: required by rule 2
// new Point(1,2).equals(new Point(1,2))                   // true

go deeper

for a junior

Can state the headline rule: equal objects must have equal hashCodes, and a code is stable while the object doesn't change. Knows hashCode returns an int used by HashMap/HashSet.

for a middle

States all three clauses (consistency, equals-link, collisions allowed) and explains why violating the equals-link loses entries in a HashMap. Knows to derive hashCode from the same fields as equals.

for a senior

Articulates the negative space too — no cross-run stability, equal codes don't imply equality, default is a per-run identity hash (not the address). Can reason about distribution quality vs. mere correctness.

for a principal

Frames it as an API invariant other code depends on; discusses caching hashCode for immutable types, collision-resistance vs. performance, and the security angle (predictable hashes enabling hash-flooding DoS).

## What hashCode() is Every Java object inherits a method `int hashCode()` from `java.lang.Object`. It returns a 32-bit integer (`int`) meant to be a *condensed numeric fingerprint* of the object. Its primary consumer is the family of **hash-based collections** — `HashMap`, `HashSet`, `Hashtable`, `ConcurrentHashMap` — which store entries in an array of "buckets." To decide which bucket an object goes in, the collection takes the object's hashCode and maps it (via further mixing and a modulo by the array length) to a bucket index. A good hashCode spreads objects evenly across buckets so each bucket holds few items and lookups stay close to O(1). ## The contract, rule by rule The Javadoc of `Object.hashCode()` states a **contract** every override must obey: 1. **Self-consistency (within one run).** Whenever `hashCode()` is invoked more than once on the *same* object during a single execution of an application, it must consistently return the same integer — **provided no information used in `equals` comparisons is modified**. The qualifier matters: if you mutate a field that participates in equals/hashCode, the code may (and usually will) change. 2. **The equals link (the load-bearing rule).** If two objects are equal according to `equals(Object)`, then calling `hashCode()` on each **must** produce the same integer result. Formally: `a.equals(b) == true` ⟹ `a.hashCode() == b.hashCode()`. 3. **The collision allowance.** It is **not** required that unequal objects produce distinct hashCodes. Two objects that are *not* equal may legally return the same int — this is a **collision**. The Javadoc only *encourages* distinct results for unequal objects because doing so improves the performance of hash tables; it is not a correctness requirement. ## What the contract deliberately does NOT promise - **No cross-run stability.** A hashCode is only required to be consistent *within a single JVM execution*. The same object (same logical value) may return a different int the next time you start the program. This is most visible with the **default identity hash** (see below), which `Object` derives per-run and is *not* the memory address. Therefore you must never persist a hashCode to disk or send it across the network expecting it to match later. - **No reverse implication.** Equal hashCodes do **not** imply equal objects. `a.hashCode() == b.hashCode()` tells you nothing about `a.equals(b)`; it just means they may land in the same bucket, where `equals` is then used to tell them apart. ## The default identity hash If a class does **not** override `hashCode()`, it inherits `Object.hashCode()`, which returns an **identity hash code**: a value associated with the object's identity (each distinct object instance), computed and cached by the JVM. It is *not* the memory address (the GC can move objects), but a per-object, per-run number. Consequently the default `hashCode` is consistent with the default `equals` (which is reference identity `==`): two references to the *same* object give the same code; two *distinct* objects give (almost always) different codes, even if their fields are identical. ## Why the equals link matters in practice When you `put` a key into a `HashMap`, it computes the key's hashCode to choose a bucket. When you later `get` with an *equal* key, it recomputes the hashCode to find the bucket, then walks that bucket comparing with `equals`. If two equal objects produced *different* hashCodes, the lookup would probe the wrong bucket and the entry would appear missing — the object becomes "lost" in the collection. That is the concrete failure mode of violating rule 2, and it is why overriding `equals` *obligates* you to override `hashCode` consistently. ## How to satisfy it Derive the hashCode from **exactly the same fields** that `equals` uses, and from no others. The simplest correct implementation in modern Java is `java.util.Objects.hash(field1, field2, ...)`. Records generate a compliant `hashCode` automatically from their components.

  • If two objects have the same hashCode, must they be equal?
    No. Equal hashCodes do not imply equality — that is a collision, which the contract explicitly permits. The collection still calls equals() to distinguish them. Only the reverse holds: equal objects must have equal hashCodes.
  • Can you cache a hashCode value to disk and rely on it next run?
    No. The contract only guarantees consistency within a single JVM run. Across runs the value may differ (especially the default identity hash), so persisting it is unsafe. Recompute it from the object's state each run instead.

A hashCode is like the first letter of a surname used to file documents in labeled drawers. Two identical documents must file under the same letter (equal -> equal code). Different surnames can share a letter (collisions allowed). And the filing scheme is only valid for this office's setup today — another office may letter things differently (no cross-run stability).

saying these in an interview costs you the question

  • Claiming hashCode must be unique for unequal objects (collisions are allowed)
  • Claiming equal hashCodes mean the objects are equal
  • Believing hashCode is the object's memory address
  • Assuming hashCode is stable across JVM restarts
  • Saying you can override equals without touching hashCode

context

open as a page

Why can mutating a field used in hashCode() make an object disappear from a HashSet, given the consistency clause?

level: middleimportance: must knowfreq 60%

basics

~20 s

HashSet remembers the bucket it put the object in, chosen from the hashCode at insert time. If you change a field that hashCode uses, the new hashCode points to a different bucket — so contains() looks in the wrong place and the object seems gone.

open as a page

What does the default Object.hashCode() return, and what does 'not stable across JVM runs' mean for it?

level: middleimportance: should knowfreq 40%

basics

~20 s

If a class doesn't override hashCode(), it gets an identity hash: a number tied to that specific object instance, the same every time you ask during one run. It is not the memory address, and a fresh run of the program can produce different numbers.

open as a page

Beyond correctness, what makes a hashCode() implementation 'good', and why does it matter for HashMap performance?

level: seniorimportance: should knowfreq 55%

basics

~20 s

A correct hashCode just needs equal objects to share a code. A good one also spreads unequal objects across many different codes, so HashMap buckets stay small and lookups stay fast. A poor spread piles items into few buckets and slows lookups.

open as a page

When equals/hashCode are inherited across a class hierarchy, how can a subclass adding a field break the hashCode contract, and how do you avoid it?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

If a subclass adds a field and changes equals/hashCode to use it, a parent object and a child object can end up 'equal' one way but with different hashCodes, breaking the rule that equal objects share a code. Avoid it by using composition instead of extending value classes, or by comparing exact classes.

open as a page