Equals and HashCode
equals
Method
equals
MethodThe equals
method is used to check for logical equality between two objects, not reference equality. By default, equals
in the Object
class checks if two references point to the same memory address (this == obj
).
Overriding equals
equals
When overriding equals
, it’s essential to ensure that it satisfies certain properties:
Reflexive: For any non-null reference
x
,x.equals(x)
should returntrue
.Symmetric: For any non-null references
x
andy
,x.equals(y)
should returntrue
if and only ify.equals(x)
istrue
.Transitive: For any non-null references
x
,y
, andz
, ifx.equals(y)
istrue
andy.equals(z)
istrue
, thenx.equals(z)
should betrue
.Consistent: Multiple calls to
x.equals(y)
should consistently returntrue
orfalse
, provided that no fields used in the comparison are modified.Non-null: For any non-null reference
x
,x.equals(null)
should returnfalse
.
Implementing equals
equals
A typical equals
implementation in Java involves the following steps:
Reference Check: Return
true
if the references are identical (this == obj
).Null Check: Return
false
if the object is null.Class Check: Return
false
if the classes differ, ensuringequals
works only within the same class.Cast and Field Comparison: Cast the object to the correct type, then compare fields.
Example:
hashCode
Method
hashCode
MethodThe hashCode
method returns an integer hash code that represents the object’s address in memory (default behavior) or a calculated value based on its fields. Hash-based collections like HashMap
and HashSet
rely on hashCode
for quickly locating and storing objects.
hashCode
Contract
hashCode
ContractWhen overriding equals
, we must also override hashCode
to maintain consistency in hash-based collections. The hashCode
contract states:
If two objects are equal according to
equals
, they must return the samehashCode
.If two objects are not equal, their
hashCode
can be the same or different.
Implementing hashCode
hashCode
A good
hashCode
function should evenly distribute hash codes across the hash table to minimize collisions.Use prime numbers in hash code calculations, such as
31
, to reduce hash collisions and improve distribution.
Here’s a basic implementation using name
and age
:
Or manually:
Custom Hashing Strategies
For advanced scenarios where default hash code algorithms don’t perform well, we can use custom hashing strategies or external libraries like Google Guava’s Hashing
class, especially for complex objects.
Effective Use in Large Hash-Based Collections
To optimize performance in large collections:
Ensure a balanced distribution by selecting fields that offer a unique representation of the object.
Benchmark and analyze hash collisions if the collection is very large or performance-sensitive.
Using equals
and hashCode
in Collections
equals
and hashCode
in CollectionsHash-based Collections: Collections like
HashMap
,HashSet
, andHashtable
usehashCode
for efficient storage and retrieval.Example: When we add an object to a
HashSet
, itshashCode
determines the bucket it’s placed in.equals
checks if an object with the same key already exists.Collisions: If two objects have the same
hashCode
but aren’t equal, they’re stored in the same bucket (collision), andequals
checks help identify distinct objects within the bucket.
Testing equals
and hashCode
equals
and hashCode
Testing equals
and hashCode
helps ensure correctness. Here’s how to test them:
Equality Tests: Check that
equals
adheres to reflexive, symmetric, and transitive properties.Hash Code Consistency: Verify that equal objects have the same hash code.
Non-equality: Ensure that different objects or objects with different values in significant fields return
false
forequals
and have different hash codes.
Pom dependency for test framework
equals
and hashCode
in inheritance
equals
and hashCode
in inheritanceWhen dealing with equals()
and hashCode()
in inheritance, we must ensure:
Consistency – If two objects are equal, their hash codes must be the same.
Symmetry – If
child.equals(parent)
, thenparent.equals(child)
should also be true.Liskov Substitution Principle (LSP) – A subclass object should behave correctly when treated as a superclass object.
Example 1: equals()
and hashCode()
with inheritance (Correct Approach)
Person
overridesequals()
andhashCode()
correctly.Employee
first callssuper.equals()
, ensuring it correctly compares inherited fields.hashCode()
inEmployee
includessuper.hashCode()
, ensuring consistency.
Example 2: Using instanceof
for Flexible Inheritance
If we want a flexible equals()
that allows comparing a subclass with a superclass, we can modify it like this:
Using
instanceof
instead ofgetClass()
makesequals()
flexible.Person
objects can be considered equal toEmployee
objects if their fields match.
Best Practices
Always override
hashCode
when we overrideequals
.Use immutable fields in
equals
andhashCode
whenever possible.Avoid using transient or derived fields (fields that change frequently or aren’t part of the core identity of the object).
Use IDE auto-generation or Lombok annotations to simplify correct implementation.
Test
equals
andhashCode
to confirm they fulfill their contracts.
Additional Info
Last updated
Was this helpful?