← Back to list

Understanding equals() and hashCode() in Java: Why Your HashMap Might Be Misbehaving

Why your Java HashMap or HashSet may not work as expected and how equals() and hashCode() can silently break your collections.

Ayoub Taouam · 2025-10-21 13:43 · 0 claps · 3.1 min read
#java #hashcode #equals-method-java #hashmap #hashset
Open on Medium ↗

Understanding equals() and hashCode() in Java: Why Your HashMap Might Be Misbehaving

If you’ve ever wondered why your HashMap refuses to find an object you know you just put in there, or why your HashSet suddenly contains what looks like duplicate elements, you’ve likely violated the sacred contract between equals() and hashCode().

Let’s fix that once and for all.

Why equals() and hashCode() Matter

In Java, objects are stored and retrieved in hash-based collections like HashMap, HashSet, using a two-step process:

  1. hachCode(): determines which “bucket” the object goes into.
  2. equals(): used to compare objects within the same bucket.

If either one doesn’t behave as expected, you’ll end up with:

  • Missing elements,
  • Duplicates where there shouldn’t be any,
  • Or lookups that fail even though the element “exists”.

The Contract Between equals() and hashCode()

Java defines a strict contract between these two methods:

  1. If two objects are equal according to equals(), they must have the same hashCode().
  2. If two objects have the same hashCode(), they are not necessarily equal.
  3. If equals() is overridden, you must also override hashCode() (and vice versa).
  4. The hashCode() value should be consistent, calling it multiple times on the same object (if unchanged) must return the same number.

Let’s see why this matters.

A Broken Example

Here’s a simple class that breaks the rules:

public class Person {
    private String name;
    private int age;

    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Overriding equals() only
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return age == person.age && name.equals(person.name);
    }

    // hashCode() NOT overridden!
}

Now let’s test it:

Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);

System.out.println(p1.equals(p2)); // true

HashSet<Person> set = new HashSet<>();
set.add(p1);
System.out.println(set.contains(p2)); // false

Even though p1.equals(p2) returns true, the HashSet fails to find p2.

Why? Because their hashCode() values (inherited from object) are different, so they end up in different buckets.

The Correct Implementation

To fix, we must override both methods together:

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

Now:

Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);

System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode() == p2.hashCode()); // true

HashSet<Person> set = new HashSet<>();
set.add(p1);
System.out.println(set.contains(p2)); // true

Everything works as intended.

Why HashMap and HashSet Depend on These Methods

Let’s peek under the hood.

**HashSet**

Internally, HashSet uses a HashMap, it stores your elements as keys.

When you do:

set.add(p1);

It calls hashCode() to find the correct bucket and then equals() to check for duplicates.

If your hashCode() and equals() are inconsistent, the HashSet might store duplicates in different buckets.

HashMap

A HashMap uses keys hashCode() to find the right bucket, and equals() to check if the key already exists.

For example:

Map<Person, String> map = new HashMap<>();
map.put(p1, "Developer");

System.out.println(map.get(p2)); // null

Without a proper hashCode(), it looks in the wrong place.

Once fixed, it works:

System.out.println(map.get(p2)); // "Developer"

A Word on Object.hash() and Performance

Using Objects.hash() is simple and safe, but it creates an internal array, which adds a tiny overhead.

If performance is critical, you can implement our own lightweight hash:

@Override
public int hashCode() {
    int result = name != null ? name.hashCode() : 0;
    result = 31 * result + age;
    return result;
}

The multiplier 31 is a common choice in Java (it’s an odd prime number that produces fewer collisions).

Common Pitfalls

  • Mutable fields in hash-based collections
set.add(p1);
p1.setName("Bob"); // modifying key fields
set.contains(p1); // false now!

Always use immutable fields (or avoid mutating them after insertion).

  • Relying on default implementations: The default equals() in Object checks for reference equality, not content equality, meaning only a == b returns true.
  • Forgetting symmetry or transitivity in equals() : Symmetry: a.equals(b) => b.equals(a), transitivity: if a.equals(b) and b.equals(c) => a.equals(c).

Wrap up

The equals() and hashCode() contract is one of those “simple-sounding but critical” foundations of Java. Break it, and your collections will quietly betray you. Respect it, and your HashMap and HashSet will behave like loyal data structures they were meant to be.

If you found this helpful, consider following me for more clear-headed guides on Java and Spring Boot.


메타데이터
post_id
a97b87bb4e8b
slug
understanding-equals-and-hashcode-in-java-why-your-hashmap-might-be-misbehaving-a97b87bb4e8b
url
https://medium.com/@ayoubtaouam/understanding-equals-and-hashcode-in-java-why-your-hashmap-might-be-misbehaving-a97b87bb4e8b
canonical_url
https://medium.com/@ayoubtaouam/understanding-equals-and-hashcode-in-java-why-your-hashmap-might-be-misbehaving-a97b87bb4e8b
author_url
https://medium.com/@ayoubtaouam
status
ok
fetched_at
2026-06-28 04:42:08