TL;DR: Misaligned equals() and hashCode() break collections.
Problems
The least surprise principle violation
Contract violations
Mutable key issues
Duplicate hash codes
Debugging becomes hard
Poor hash distribution
Solutions
Avoid mutable keys
Use effective hashes
Test behavior carefully
Avoid redefining equal and hash
Honor the Bijection
Context
When you work with hashed collections like HashMap or HashSet, you should pay special attention to equals() and hashCode().
A mismatch or poor implementation can lead to unpredictable bugs.
equals() method defines logical equality, while hashCode() determines an object’s bucket for faster access.
When these two methods fail to align, collections lose their reliability, leading to poor performance or issues like duplicate entries caused by hash collections.
The best solution is never to override the hash and equals and rely on object identity.
This is what happens in the real world using the MAPPER).
Whenever you get an external object you need to map it to your bijection correspondence and not create a brand new one.
Once within your controlled system, rely on identity and forget equality issues.
Sample Code
Wrong
class BrokenObject {
private int value;
public BrokenObject(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
return true; // Always equal
}
@Override
public int hashCode() {
return super.hashCode(); // Uses default implementation
}
}
Right
class FixedObject {
private final int value;
public FixedObject(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
FixedObject that = (FixedObject) obj;
return value == that.value;
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
// This is the best solution
class CleanObject {
private final int value;
public FixedObject(int value) {
this.value = value;
}
// - @Override
// - public boolean equals(Object obj) {}
// - @Override
// - public int hashCode() {
}
}
Detection
[X] Semi-Automatic
Automated linters and IDEs flag issues when you don't properly override equals() or hashCode().
Tags
Premature Optimization
Level
[x] Intermediate
AI Generation
AI-generated code often missteps when generating equals() and hashCode(), especially for mutable objects.
AI Detection
AI tools can help fix this smell with minimal guidance.
Try Them!
Remember: AI Assistants make lots of mistakes
Without Proper Instructions
With Specific Instructions
Conclusion
When you misuse equals() or hashCode(), collections misbehave.
Stick to their contracts, use effective hashes, and avoid mutable keys.
Relations
Code Smell 150 - Equal Comparison
Code Smell 167 - Hashing Comparison
More Info
Disclaimer
Code Smells are my opinion.
Credits
Photo by frank mckenna on Unsplash
“Bad programmers worry about the code. Good programmers worry about data structures and their relationships.”
Linus Torvalds
Software Engineering Great Quotes
This article is part of the CodeSmell Series.