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.
Auto-Generated equals and hashCode
Modern IDEs (like IntelliJ and Eclipse) can auto-generate equals and hashCode based on fields we select. This reduces the chance of errors and ensures a correct initial implementation.
Libraries (Lombok)
Lombok’s @EqualsAndHashCode annotation can automatically generate equals and hashCode, helping simplify our code.
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.
HashSet: HashSet uses hashCode to place objects in buckets. When checking for duplicates, it first checks hashCode to locate the bucket, then uses equals to confirm object equality within the bucket.
HashMap: In HashMap, the hashCode of the key is used to locate the correct bucket. Within the bucket, equals is used to find the exact key.
Mutable Fields
Using mutable fields in
equalsandhashCodeis risky. If a field changes, the object’shashCodewill change, making it “disappear” from collections likeHashMaporHashSet.Solution: Use immutable fields in our
equalsandhashCodeimplementations. If this isn’t possible, avoid modifying fields while the object is in a hash-based collection.
Consistency in Subclasses
If
equalsandhashCodeare overridden in a subclass, we risk breaking symmetry if both subclass and superclass instances are compared.Solution: If a class hierarchy uses
equalsandhashCode, consider markingequalsasfinalor using delegation.
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