Why Overriding hashCode and equals Is Critical: Avoid Memory Leaks and Bucket Chaos in Java

This article explains the contract between hashCode() and equals() in Java, shows the problems caused by violating it—such as incorrect behavior in HashMap/HashSet and memory leaks—and provides practical guidelines and code examples for implementing robust hashCode methods, including the use of Apache's HashCodeBuilder.

Programmer DD
Programmer DD
Programmer DD
Why Overriding hashCode and equals Is Critical: Avoid Memory Leaks and Bucket Chaos in Java

1. Overriding hashCode() and equals() Contract

Every Java object has two important methods, hashCode() and equals(), which should be overridden according to a well‑defined contract when objects are used as keys in collections like HashMap or HashSet. This article describes why and how to correctly override hashCode().

1.1 hashCode Contract

If two objects are equal, their hashCode() methods must return the same hash value.

Two questions arise: what happens if equal objects return different hash codes, and what if unequal objects return the same hash code?

Equal objects with different hashCode() values.

Unequal objects with identical hashCode() values.

1.1.1 Equal objects but different hashCode

If you store such objects in a HashSet or HashMap, the collection may behave unexpectedly because it relies on the hash code to locate the bucket where the object is stored.

In a HashMap, the put() operation stores the entry in a bucket determined by the object's hash code. When contains() or get() is called, the map recomputes the hash code, looks in the corresponding bucket, and compares the key with existing entries using equals(). If the hash code has changed, the lookup fails.

1.1.2 Unequal objects but same hashCode

The contract does not require distinct objects to have different hash codes, so collisions are allowed. However, many collisions degrade the performance of hash‑based collections.

1.2 Why Buckets?

Without buckets, a collection would have to scan every element to find a match. Buckets allow the collection to examine only the small subset of entries that share the same hash code, improving efficiency.

1.3 Implementing hashCode()

Writing a good hashCode() method is tricky. A naïve implementation that always returns a constant (e.g., return 1;) satisfies the contract but forces all entries into a single bucket, destroying performance.

1.3.1 Returning a Constant Value

@Override
public int hashCode() {
    return 1;
}

This approach is legal but results in poor performance because every object ends up in the same bucket.

1.3.2 Effective Java Guidelines

Joshua Bloch recommends the following pattern (from *Effective Java*):

Start with a non‑zero constant, e.g., int result = 17;.

For each significant field f, compute an int c and combine it: result = 37 * result + c; Return result.

The computation of c depends on the field type:

boolean → c = (f ? 1 : 0) byte, char, short, int → c = (int) f long → c = (int)(f ^ (f >>> 32)) float → c = Float.floatToIntBits(f) double →

long l = Double.doubleToLongBits(f); c = (int)(l ^ (l >>> 32))

object reference → c = f.hashCode() array → treat each element as a separate field and combine recursively.

Finally, combine all c values using the formula above and return the result.

1.3.3 Apache HashCodeBuilder

The Apache Commons HashCodeBuilder simplifies the implementation:

@Override
public int hashCode() {
    return new HashCodeBuilder(83, 7)
        .append(field1)
        .append(field2)
        .toHashCode();
}

The two constructor arguments are non‑zero odd numbers that help reduce collisions.

1.4 Mutable Objects as Keys

It is recommended to use immutable objects as keys in collections. If a mutable object's state changes after it has been placed in a hash‑based collection, its hash code may change, causing the object to be stored in the wrong bucket and making look‑ups fail.

Example:

public class Employee {
    private String name;
    private int age;
    // getters, setters omitted
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Employee) {
            Employee e = (Employee) obj;
            return name.equals(e.name) && age == e.age;
        }
        return false;
    }
    @Override
    public int hashCode() {
        return name.length() + age;
    }
    public static void main(String[] args) {
        Employee e = new Employee("muhammad", 24);
        Map<Object, Object> m = new HashMap<>();
        m.put(e, "value");
        System.out.println(m.get(e)); // works
        e.name = "abid"; // mutates key
        System.out.println(m.get(e)); // fails
    }
}

To avoid this, either keep keys immutable or remove the object from the collection before mutating it and re‑insert it afterwards.

1.5 Memory Leaks with hashCode and equals

If equals() and hashCode() are not overridden correctly, a HashMap can retain references to objects indefinitely, leading to memory leaks and eventually an OutOfMemoryError. Example:

public class HashcodeLeakExample {
    private String id;
    public HashcodeLeakExample(String id) { this.id = id; }
    public static void main(String[] args) {
        Map<HashcodeLeakExample, String> map = new HashMap<>();
        while (true) {
            map.put(new HashcodeLeakExample("id"), "value");
        }
    }
}

Because each new instance is considered a distinct key (default Object.equals() and hashCode()), the map never discards old entries, causing unbounded growth.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javamemory leakHashMapImmutable Objectshashsetequalshashcode
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.