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
equalsWhen 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
xandy,x.equals(y)should returntrueif and only ify.equals(x)istrue.Transitive: For any non-null references
x,y, andz, ifx.equals(y)istrueandy.equals(z)istrue, thenx.equals(z)should betrue.Consistent: Multiple calls to
x.equals(y)should consistently returntrueorfalse, 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
equalsA typical equals implementation in Java involves the following steps:
Reference Check: Return
trueif the references are identical (this == obj).Null Check: Return
falseif the object is null.Class Check: Return
falseif the classes differ, ensuringequalsworks 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
hashCodecan be the same or different.
Implementing hashCode
hashCodeA good
hashCodefunction 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, andHashtableusehashCodefor efficient storage and retrieval.Example: When we add an object to a
HashSet, itshashCodedetermines the bucket it’s placed in.equalschecks if an object with the same key already exists.Collisions: If two objects have the same
hashCodebut aren’t equal, they’re stored in the same bucket (collision), andequalschecks help identify distinct objects within the bucket.
Testing equals and hashCode
equals and hashCodeTesting equals and hashCode helps ensure correctness. Here’s how to test them:
Equality Tests: Check that
equalsadheres 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
falseforequalsand 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)
Personoverridesequals()andhashCode()correctly.Employeefirst callssuper.equals(), ensuring it correctly compares inherited fields.hashCode()inEmployeeincludessuper.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
instanceofinstead ofgetClass()makesequals()flexible.Personobjects can be considered equal toEmployeeobjects if their fields match.
Best Practices
Always override
hashCodewhen we overrideequals.Use immutable fields in
equalsandhashCodewhenever 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
equalsandhashCodeto confirm they fulfill their contracts.
Additional Info
Object Equality CheckLast updated