Skip to content
Java

== vs equals() and hashCode() in Java, Explained

By The EbookWale Team · Updated June 16, 2026 · 4 min read

In Java, == compares references while equals() compares values — and if you override equals() you must override hashCode() too. Here's why, the contract between them, and how HashMap depends on it.

In Java, == compares references (are these the same object?) while equals() compares values (are these objects meaningfully equal?) — and the moment you override equals(), you must override hashCode() too. Getting this wrong silently breaks HashMap and HashSet, which is why it’s one of the most-asked Java interview topics. It’s a Phase 4 fundamental from the Java roadmap.

== compares references

== checks whether two variables point to the same object in memory:

String a = new String("hi");
String b = new String("hi");
a == b;            // false — two different objects
a.equals(b);       // true  — same value

For primitives (int, boolean), == compares values directly and is correct. For objects, == only asks “is it literally the same object?” — rarely what you mean.

Ana @1 Ana @2 "Ana" "Ana" == .equals() false (different objects) true (same value)
== compares identity; .equals() compares value.

equals() compares values

equals() defines meaningful equality. Object’s default equals() just does ==, so you override it to compare the fields that matter:

@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;     // value equality
}

Now two Point(3, 4) objects are equal even though they’re different objects.

The hashCode() contract

Here’s the rule people forget: if you override equals(), you must override hashCode(), and equal objects must return equal hash codes.

@Override
public int hashCode() {
    return Objects.hash(x, y);       // consistent with equals()
}

Why it matters: hash-based collections work in two steps — hashCode() picks a bucket, then equals() confirms the match. If two “equal” objects have different hash codes, they land in different buckets and the collection can’t find them.

Set<Point> seen = new HashSet<>();
seen.add(new Point(3, 4));
seen.contains(new Point(3, 4));   // false if hashCode() not overridden!

This is the bug: override equals() but not hashCode(), and your objects mysteriously “vanish” from sets and maps. See the Collections framework.

🔑 REMEMBER — == = same object; equals() = same value. Override them as a pair — equal objects must have equal hash codes, or HashMap/HashSet break. Modern Java's records generate both correctly for free.

Strings and the pool

Strings make == especially treacherous because of the string pool: identical string literals may share one object, so == sometimes appears to work — then fails for strings built at runtime:

String a = "hi";
String b = "hi";
a == b;                        // true — both from the pool
String c = new String("hi");
a == c;                        // false — c is a new object
a.equals(c);                   // true — always use equals() for strings

Always compare string contents with equals().

The full equals contract

A correct equals() is:

  • Reflexive: x.equals(x) is true.
  • Symmetric: x.equals(y)y.equals(x).
  • Transitive: if x=y and y=z, then x=z.
  • Consistent: repeated calls give the same result.
  • Consistent with hashCode: equal objects share a hash code.

Common mistakes

  • Overriding equals() but not hashCode() — the silent HashMap/HashSet bug.
  • Using == to compare strings or objects — compares identity, not value.
  • A hashCode() inconsistent with equals() — uses different fields, breaking lookups.
  • Hand-writing both when a record would generate them correctly — let the language do it for data classes.

Where this fits

==, equals(), and hashCode() are Phase 4 of the Java roadmap, essential to object-oriented Java and to using the Collections framework correctly.

The contract and correct implementations are drawn out in Java in One Month and the job-ready Java in Three Months; the deeper details — hash distribution, immutability, and identity at scale — are in Java for Staff Engineers.

== for identity, equals() for value, and always override hashCode() alongside equals() — that pairing keeps your collections honest.

Frequently asked questions

What is the difference between == and equals() in Java?

== compares references — whether two variables point to the exact same object in memory. equals() compares values — whether two objects are meaningfully equal, as defined by the class. For objects you almost always want equals(); == only checks identity, which is rarely what you mean for things like strings.

Why must you override hashCode() when you override equals()?

Because hash-based collections like HashMap and HashSet use hashCode() to find the right bucket and equals() to confirm a match. If two objects are equal by equals() but return different hashCodes, the collection looks in the wrong bucket and can't find them. The contract: equal objects must have equal hash codes.

Why should I use equals() instead of == for strings?

Because == checks whether two String variables are the same object, not whether they contain the same text. Due to string pooling, some equal strings share an object and some don't, so == is unreliable. Always use equals() to compare string contents.

What is the equals and hashCode contract in Java?

The contract states: if two objects are equal by equals(), they must return the same hashCode(); equal objects stay equal (consistency); and equals must be reflexive, symmetric, and transitive. Unequal objects may share a hashCode (a collision), but equal objects must never have different hash codes.