== vs equals() and hashCode() in Java, Explained
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.
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.
== = 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=yandy=z, thenx=z. - Consistent: repeated calls give the same result.
- Consistent with hashCode: equal objects share a hash code.
Common mistakes
- Overriding
equals()but nothashCode()— the silentHashMap/HashSetbug. - Using
==to compare strings or objects — compares identity, not value. - A
hashCode()inconsistent withequals()— 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.