# 问题
3. 为什么 HashMap 需要重写 hashCode()
和 equals()
方法?
# 标准答案
在 HashMap 中,hashCode()
和 equals()
方法用于确定 key 在数组中的存储位置,并在哈希冲突时正确匹配 key。
hashCode()
决定 key 的哈希值,并通过(n - 1) & hash
计算存储位置。equals()
用于比较 key 是否相等,保证 HashMap 在哈希冲突时能正确匹配 key,而不会误判不同 key。
如果 hashCode()
或 equals()
没有正确重写,可能导致 相同对象存储到不同位置、不同对象被误认为相同、get() 失败、数据丢失等问题。
# 答案解析
# 1. HashMap 的存取流程
HashMap 通过 哈希函数 + 数组 + 链表(或红黑树) 存储键值对,存取过程如下:
- 计算 key 的
hashCode()
,用(h = key.hashCode()) ^ (h >>> 16)
计算哈希值,减少冲突。 - 计算 key 的存储索引,用
(n - 1) & hash
确定数组索引。 - 插入 key-value:
- 如果桶为空,直接插入。
- 如果桶不为空,则遍历链表/红黑树:
equals()
判断 key 是否已存在,相等则更新 value。- 否则插入到链表尾部或转换为红黑树。
- 查找 key 时:
- 先通过
hashCode()
定位桶。 - 再通过
equals()
遍历该桶的链表/红黑树查找 key。
- 先通过
# 2. 为什么 hashCode()
需要重写?
hashCode()
直接影响 key 的 存储位置,如果不同对象的hashCode()
相同,容易导致 哈希冲突,影响 HashMap 性能。- 默认的
Object.hashCode()
可能不适用于自定义对象。例如:由于class User { String name; }
1
2
3User
没有重写hashCode()
,它默认使用Object.hashCode()
,不同User
对象即使name
相同,也会有不同的hashCode()
,导致 HashMap 认为它们是不同 key。
✅ 正确写法:
@Override
public int hashCode() {
return Objects.hash(name);
}
1
2
3
4
2
3
4
❌ 错误写法:
@Override
public int hashCode() {
return 1; // 导致所有 key 产生相同哈希值,退化为链表,查询性能降至 O(n)
}
1
2
3
4
2
3
4
# 3. 为什么 equals()
需要重写?
equals()
用于 哈希冲突后,确定 key 是否相等,确保get()
时能正确找到 key。- 如果
equals()
没有正确实现,可能导致 相同 key 被认为不同,导致 put() 重复插入,get() 失败。
✅ 正确写法:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return Objects.equals(name, user.name);
}
1
2
3
4
5
6
7
2
3
4
5
6
7
❌ 错误写法(忽略 null
或类型转换错误):
@Override
public boolean equals(Object obj) {
return this == obj; // 只判断内存地址,不符合业务需求
}
1
2
3
4
2
3
4
# 4. HashMap 的常见问题
❌ 常见误区
误认为
hashCode()
唯一就不需要equals()
- 错误示例:
@Override public int hashCode() { return id; // 仅使用 ID }
1
2
3
4 - **问题:**如果
equals()
未重写,HashMap 仍然无法正确判断 key 是否相等,导致数据重复插入。
- 错误示例:
误认为
hashCode()
相同的对象一定相等- 由于哈希冲突,不同对象可能有相同的
hashCode()
,但equals()
仍需判断是否真的相等。
- 由于哈希冲突,不同对象可能有相同的
误认为
put()
方法会自动去重put()
依赖equals()
判断 key 是否已存在,否则可能导致重复 key 仍然插入。
# 深入追问
🔹 为什么 hashCode()
不能直接返回对象地址?
🔹 为什么 HashMap 不能只使用 hashCode()
进行 key 查找?
🔹 hashCode()
和 equals()
如何优化 HashMap 的查询性能?
🔹 如果 hashCode()
计算过于复杂,会对 HashMap 造成什么影响?
# 相关面试题
• hashCode()
和 equals()
的默认实现是什么?
• hashCode()
和 equals()
违反一致性时会导致什么问题?
• HashMap 在高并发下如何保证数据一致性?
• TreeMap
为什么只需要 compareTo()
而不需要 hashCode()
?