The equals() and hashCode() Contract
equals() and hashCode() methods are the foundation of how Java collections work. Get them wrong, and objects "disappear" from HashMaps, duplicates appear in HashSets, and tests fail mysteriously. Get them right, and your collections behave predictably and efficiently.This is the #1 most-asked topic in Java interviews for good reason - it reveals whether a developer truly understands how collections work under the hood.
π At a Glance
| Aspect | Details |
|---|---|
| Core Methods | equals(), hashCode() from Object class |
| Related | Comparable.compareTo(), Comparator.compare() |
| Contract | Mathematical properties that must hold |
| Impact | HashSet, HashMap, TreeSet, TreeMap behavior |
| Key Insight | equals() = true βΉ hashCode() must be same |
π― What You'll Learn
After reading this article, you will be able to:
- Explain the contracts for equals(), hashCode(), and Comparable
- Implement correct equals/hashCode for any class
- Identify bugs when contracts are violated
- Understand why mutable keys cause problems in HashMaps
- Use modern alternatives like Records and Lombok safely
- Debug issues where objects "disappear" from collections
π₯ Production Story: The Vanishing Orders
An e-commerce platform had a critical bug: orders were "disappearing" from the cache. Customer service was getting complaints about orders not appearing in the dashboard, even though they existed in the database.
After two days of debugging, the team found the culprit:
JAVA(23 lines)CodeLoading syntax highlighter...
HashSet<Order>. Here's what happened:JAVA(14 lines)CodeLoading syntax highlighter...
JAVA(18 lines)CodeLoading syntax highlighter...
- Only include immutable fields in equals/hashCode
- For entities, identity (ID) is usually the only field needed
- Mutable fields in hash calculations = time bombs
π§ Mental Model: The Library Card Catalog
hashCode() and equals() like a library's card catalog system:TEXT(26 lines)CodeLoading syntax highlighter...
hashCode() is for speed (narrow down the search), equals() is for correctness (confirm exact match).π¬ Deep Dive
1. The equals() Contract
equals() method must satisfy these mathematical properties:JAVA(23 lines)CodeLoading syntax highlighter...
JAVA(30 lines)CodeLoading syntax highlighter...
JAVA(22 lines)CodeLoading syntax highlighter...
2. The hashCode() Contract
JAVA(21 lines)CodeLoading syntax highlighter...
JAVA(9 lines)CodeLoading syntax highlighter...
Why 31?
- Odd prime: Primes give better distribution than even numbers
- One less than power of 2:
31 * x=(x << 5) - x(fast bit shift optimization) - Good distribution: Empirically produces fewer collisions for string-like data
TEXT(5 lines)CodeLoading syntax highlighter...
3. The Relationship: equals() and hashCode()
TEXT(23 lines)CodeLoading syntax highlighter...
4. What Happens When You Break the Contract
JAVA(31 lines)CodeLoading syntax highlighter...
5. Implementing equals() and hashCode() Correctly
JAVA(25 lines)CodeLoading syntax highlighter...
getClass() instead of instanceof?JAVA(8 lines)CodeLoading syntax highlighter...
Rule of thumb:
- Use
getClass()for concrete classes (most cases) - Use
instanceofonly when explicitly designed for inheritance
6. Modern Alternatives
JAVA(12 lines)CodeLoading syntax highlighter...
JAVA(17 lines)CodeLoading syntax highlighter...
JAVA(7 lines)CodeLoading syntax highlighter...
7. Comparable and Comparator
JAVA(13 lines)CodeLoading syntax highlighter...
JAVA(21 lines)CodeLoading syntax highlighter...
JAVA(24 lines)CodeLoading syntax highlighter...
JAVA(17 lines)CodeLoading syntax highlighter...
β οΈ Common Mistakes
Mistake 1: Mutable Fields in equals/hashCode
JAVA(18 lines)CodeLoading syntax highlighter...
JAVA(11 lines)CodeLoading syntax highlighter...
Mistake 2: Using == for Object Comparison
JAVA(7 lines)CodeLoading syntax highlighter...
JAVA(2 lines)CodeLoading syntax highlighter...
Mistake 3: Forgetting Null Handling
JAVA(5 lines)CodeLoading syntax highlighter...
JAVA(7 lines)CodeLoading syntax highlighter...
Mistake 4: Inconsistent Fields in equals vs hashCode
JAVA(14 lines)CodeLoading syntax highlighter...
JAVA(10 lines)CodeLoading syntax highlighter...
Mistake 5: Breaking Symmetry with Inheritance
JAVA(28 lines)CodeLoading syntax highlighter...
JAVA(12 lines)CodeLoading syntax highlighter...
π Debug This: The Memory Leak
Session objects in a HashSet<Session> that should have been removed:JAVA(47 lines)CodeLoading syntax highlighter...
lastAccess field is mutable and included in equals() and hashCode(). When touch() is called:- Session's
lastAccesschanges hashCode()returns different value- Session is now in wrong bucket
remove()looks in new bucket, doesn't find it- Session is "stuck" in the old bucket β memory leak
JAVA(19 lines)CodeLoading syntax highlighter...
π» Exercises
Exercise 1: Implement Employee equals/hashCode
ββ Difficulty: Medium | β±οΈ Time: 15 minutes
equals() and hashCode() for an Employee class.JAVA(10 lines)CodeLoading syntax highlighter...
JAVA(42 lines)CodeLoading syntax highlighter...
Exercise 2: Find the Bug
βββ Difficulty: Medium | β±οΈ Time: 15 minutes
JAVA(27 lines)CodeLoading syntax highlighter...
There are multiple bugs:
JAVA(28 lines)CodeLoading syntax highlighter...
- Missing
this == ooptimization - Missing null check in equals (would NPE on null argument)
namespace.equals()would NPE if namespace is nullversionin equals but NOT in hashCode (inconsistent fields!)- Using
+instead of proper hash combination (more collisions)
Exercise 3: Comparator Chain
ββ Difficulty: Medium | β±οΈ Time: 10 minutes
JAVA(13 lines)CodeLoading syntax highlighter...
JAVA(38 lines)CodeLoading syntax highlighter...
Exercise 4: Value Object with Record
ββ Difficulty: Medium | β±οΈ Time: 10 minutes
JAVA(8 lines)CodeLoading syntax highlighter...
JAVA(39 lines)CodeLoading syntax highlighter...
Exercise 5: Consistent with Equals
ββββ Difficulty: Hard | β±οΈ Time: 20 minutes
Version class that is Comparable and consistent with equals:JAVA(11 lines)CodeLoading syntax highlighter...
JAVA(80 lines)CodeLoading syntax highlighter...
π Summary & Key Takeaways
The Contracts
| Method | Key Properties |
|---|---|
| equals() | Reflexive, Symmetric, Transitive, Consistent, nullβfalse |
| hashCode() | Consistent, equal objectsβsame hash |
| compareTo() | Antisymmetric, Transitive, should be consistent with equals |
The Golden Rules
- Override both or neither: If you override
equals(), overridehashCode() - Use same fields: Include exactly the same fields in both methods
- Immutable fields only: Never include mutable fields in hash calculations
- Consistent with equals:
compareTo()returning 0 should meanequals()is true
Quick Implementation Guide
JAVA(15 lines)CodeLoading syntax highlighter...
Modern Alternatives
| Approach | When to Use |
|---|---|
| Record | Value objects, DTOs, immutable data |
| Lombok | Legacy classes, entities with selected fields |
| IDE-generated | Most cases, review the output |
| Manual | Complex inheritance, performance-critical code |
π€ Senior-Level Interview Questions
Question #1: What happens if you break the hashCode contract?
equals() returns true but hashCode() returns different values, objects that should be "found" in HashMap/HashSet won't be. The collection uses hashCode to determine which bucket to look in. If two equal objects have different hashes, they'll be in different buckets, and contains() or get() will fail even though the object is present. Additionally, you can add "duplicates" to a HashSet because it doesn't find the existing equal object.Question #2: Why shouldn't you use mutable objects as HashMap keys?
get() with the modified key looks in the wrong bucket and returns null. remove() also fails. The object is stuck, causing a memory leak. Even iterating won't help because you can't remove by key. The only solution is to rebuild the entire map.Question #3: What's the difference between Comparable and Comparator?
-
Comparable defines the natural ordering of a class. It's implemented by the class itself via
compareTo(T other). There's only one natural ordering per class. Example:Stringis naturally ordered alphabetically. -
Comparator is an external comparison strategy. It's a separate object that compares two instances via
compare(T o1, T o2). You can have many comparators for different sort orders. Example: Sort employees by name, salary, or hire date using different Comparators.
Use Comparable for the obvious/default ordering, Comparator for alternative orderings.
Question #4: What does "consistent with equals" mean for Comparable?
(x.compareTo(y) == 0) == x.equals(y) for all objects. If two objects are equal according to compareTo() (returning 0), they should also be equal according to equals(), and vice versa.compareTo() for equality, not equals(). If they're inconsistent, TreeSet behavior differs from HashSet. Classic example: BigDecimal where new BigDecimal("1.0").equals(new BigDecimal("1.00"))compareTo() returns 0 (same numeric value).Question #5: How do Java Records implement equals and hashCode?
equals() and hashCode() based on all record components (the constructor parameters). The generated equals() compares all components for equality using Objects.equals(). The generated hashCode() combines all components using the same algorithm as Objects.hash().Important: These implementations use ALL components. If you want to exclude certain fields from equality, you cannot use a Record - use a regular class instead. Also, record components should be immutable (or at least their identity should be), since they're all included in hashCode.
Question #6: When can two different objects have the same hashCode?
"FB".hashCode() == "Ea".hashCode() (both are 2236). HashMap handles this via chaining or tree bins for buckets with many collisions.π Conclusion
equals() and hashCode() contract is fundamental to how Java collections work. Master it, and you'll:- Avoid subtle bugs where objects "disappear" from collections
- Prevent memory leaks caused by stuck objects
- Write correct entity classes that work properly in Sets and as Map keys
- Ace technical interviews on this frequently-asked topic
- Always override both
equals()andhashCode()together - Use only immutable fields that define object identity
- For entities, ID/primary key is usually sufficient
- Consider using Records for value objects
- When using Comparable, keep it consistent with equals
π Review Schedule for This Article
| Day | Task | Time |
|---|---|---|
| Day 1 | Review the golden rules and contracts | 5 min |
| Day 3 | Redo Exercise 1 (Employee equals/hashCode) | 15 min |
| Day 7 | Answer interview questions without looking | 10 min |
| Day 14 | Redo Debug This exercise (Memory Leak) | 10 min |
| Day 30 | Review mental model and quick reference | 5 min |