You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
63. ### What are common pitfalls with equals() and hashCode() methods?
4438
+
4439
+
The `equals()` and `hashCode()` methods are fundamental to how Java handles object comparison and hash-based collections. Incorrectly implementing them leads to subtle and hard-to-debug issues. Below are the most common pitfalls:
4440
+
4441
+
**1.Overriding equals() without overriding hashCode()**
4442
+
4443
+
This is the most common mistake. The**contract** states:if two objects are equal according to `equals()`, they **must** have the same `hashCode()`.Violatingthis breaks hash-based collections like `HashMap`, `HashSet`, and `Hashtable`.
4444
+
4445
+
```java
4446
+
publicclassEmployee {
4447
+
privateint id;
4448
+
privateString name;
4449
+
4450
+
publicEmployee(intid, Stringname) {
4451
+
this.id = id;
4452
+
this.name = name;
4453
+
}
4454
+
4455
+
@Override
4456
+
publicbooleanequals(Objectobj) {
4457
+
if (this== obj) returntrue;
4458
+
if (obj ==null|| getClass() != obj.getClass()) returnfalse;
4459
+
Employee other = (Employee) obj;
4460
+
return id == other.id &&Objects.equals(name, other.name);
4461
+
}
4462
+
4463
+
// hashCode() is NOT overridden!
4464
+
}
4465
+
4466
+
publicclassTest {
4467
+
publicstaticvoidmain(String[] args) {
4468
+
Employee e1 =newEmployee(1, "John");
4469
+
Employee e2 =newEmployee(1, "John");
4470
+
4471
+
System.out.println(e1.equals(e2)); // true
4472
+
4473
+
Set<Employee> set =newHashSet<>();
4474
+
set.add(e1);
4475
+
set.add(e2);
4476
+
System.out.println(set.size()); // 2 (Expected 1, since they are "equal")
Since `hashCode()` is not overridden, `e1` and `e2` get different hash codes (from `Object.hashCode()`), so the `HashSet` treats them as different objects and `HashMap` cannot find the value using `e2`.
4486
+
4487
+
**2.Using mutable fields in hashCode()**
4488
+
4489
+
If mutable fields used in `hashCode()` are modified after the object is placed in a hash-based collection, the object becomes "lost" — it can no longer be found because its hash bucket has changed.
4490
+
4491
+
```java
4492
+
publicclassEmployee {
4493
+
privateint id;
4494
+
privateString name;
4495
+
4496
+
publicEmployee(intid, Stringname) {
4497
+
this.id = id;
4498
+
this.name = name;
4499
+
}
4500
+
4501
+
publicvoidsetName(Stringname) {
4502
+
this.name = name;
4503
+
}
4504
+
4505
+
@Override
4506
+
publicbooleanequals(Objectobj) {
4507
+
if (this== obj) returntrue;
4508
+
if (obj ==null|| getClass() != obj.getClass()) returnfalse;
4509
+
Employee other = (Employee) obj;
4510
+
return id == other.id &&Objects.equals(name, other.name);
4511
+
}
4512
+
4513
+
@Override
4514
+
publicinthashCode() {
4515
+
returnObjects.hash(id, name);
4516
+
}
4517
+
}
4518
+
4519
+
publicclassTest {
4520
+
publicstaticvoidmain(String[] args) {
4521
+
Employee e1 =newEmployee(1, "John");
4522
+
4523
+
Set<Employee> set =newHashSet<>();
4524
+
set.add(e1);
4525
+
4526
+
System.out.println(set.contains(e1)); // true
4527
+
4528
+
e1.setName("Jane"); // Mutating a field used in hashCode()
4529
+
4530
+
System.out.println(set.contains(e1)); // false! Object is "lost"
4531
+
System.out.println(set.size()); // 1 (still in the set, but unreachable)
4532
+
}
4533
+
}
4534
+
```
4535
+
4536
+
**3.Usinginstanceof instead of getClass() (breaking symmetry in inheritance)**
4537
+
4538
+
Using `instanceof` in `equals()` can break the **symmetry contract** (`a.equals(b)` must equal `b.equals(a)`) when subclasses are involved.
System.out.println(a.equals(d)); // true (Dog is an instance of Animal)
4581
+
System.out.println(d.equals(a)); // false (Animal is NOT an instance of Dog)
4582
+
// Symmetry is broken!
4583
+
}
4584
+
}
4585
+
```
4586
+
4587
+
Use `getClass()` comparison instead to ensure both objects are of the exact same type.
4588
+
4589
+
**4.Not handling null and self-comparison in equals()**
4590
+
4591
+
Forgetting to handle `null` check leads to `NullPointerException`, and skipping the self-reference check (`this== obj`) misses an easy optimization.
4592
+
4593
+
```java
4594
+
// Incorrect implementation
4595
+
@Override
4596
+
publicboolean equals(Object obj) {
4597
+
Employee other = (Employee) obj; // Crashes if obj is null!
4598
+
return id == other.id && name.equals(other.name);
4599
+
}
4600
+
4601
+
// Correct implementation
4602
+
@Override
4603
+
publicboolean equals(Object obj) {
4604
+
if (this== obj) returntrue; // Self-check
4605
+
if (obj ==null|| getClass() != obj.getClass()) returnfalse; // Null & type check
4606
+
Employee other = (Employee) obj;
4607
+
return id == other.id &&Objects.equals(name, other.name); // Null-safe comparison
4608
+
}
4609
+
```
4610
+
4611
+
**5.Using equals(ClassName obj) instead of equals(Object obj) — overloading instead of overriding**
4612
+
4613
+
A very common mistake is changing the parameter type from `Object` to a specific class. This**overloads** `equals()` instead of **overriding** it, so polymorphic calls (e.g., from collections) still use `Object.equals()`.
Always use `@Override` annotation to let the compiler catchthis mistake.
4648
+
4649
+
**6.Inconsistent equals() and hashCode() — using different fields**
4650
+
4651
+
The fields used in `equals()` and `hashCode()` must be consistent. If `equals()` uses `id` and `name` but `hashCode()` only uses `id`, two objects that differ only in `name` will have the same hash code but won't be equal — this is allowed but causes performance degradation due to hash collisions. The opposite (more fields in `hashCode()` than `equals()`) **breaks the contract**.
4652
+
4653
+
```java
4654
+
public class Employee {
4655
+
private int id;
4656
+
private String name;
4657
+
4658
+
@Override
4659
+
public boolean equals(Object obj) {
4660
+
if (this == obj) return true;
4661
+
if (obj == null || getClass() != obj.getClass()) return false;
4662
+
Employee other = (Employee) obj;
4663
+
return id == other.id; // Only uses id
4664
+
}
4665
+
4666
+
@Override
4667
+
public int hashCode() {
4668
+
return Objects.hash(id, name); // Uses id AND name — BROKEN!
0 commit comments