HashSet去重依赖HashMap的key唯一性机制,通过hashCode定位桶、equals确认相等;必须同时重写二者并保证相等对象hashCode相同。

直接说核心:HashSet 去重不是靠“挨个比对”,而是靠 hashCode 定位 + equals 确认,底层完全复用 HashMap 的机制。
它其实是个“伪装成 Set 的 HashMap”
HashSet 内部没有自己存数据,而是持有一个 HashMap<E, Object>。你 add 的每个元素,都被当作这个 HashMap 的 key;而 value 固定是同一个空对象(叫 PRESENT)。因为 HashMap 本身就不允许 key 重复,所以 HashSet 天然就去重了。
- 调用
add(e),实际执行的是map.put(e, PRESENT) - 如果 put 返回
null,说明是新 key,添加成功 - 如果返回旧的
PRESENT,说明 key 已存在,不重复添加
为什么必须重写 hashCode 和 equals?
因为 HashMap 判断 key 是否重复,分两步走:
- 先算
e.hashCode(),快速定位到哈希表的某个“桶”(数组索引) - 再在这个桶里遍历所有节点,对每个已有 key 调用
e.equals(已有key)
如果两个逻辑上相等的对象(比如两个内容相同的 Student),hashCode() 返回值不同 → 被分到不同桶 → equals() 根本不会被调用 → 被当成两个不同元素加进去了。这就是没重写导致去重失效的典型原因。
立即学习“Java免费学习笔记(深入)”;
举个接地气的例子
想象一个快递柜:
- 柜子有 100 个格子(哈希表数组),每个格子贴着一个编号(由
hashCode % 100算出) - 你寄一个包裹(add 对象),先看单号末两位(hashCode),决定塞进哪个格子
- 如果格子空,直接放;如果已有一件,就打开比对寄件人+电话(equals)
- 一样 → 不收;不一样 → 挂在格子外链表/红黑树上(处理哈希冲突)
一句话总结关键点
HashSet 去重 = HashMap 的 key 唯一性保障;hashCode() 负责“找地方”,equals() 负责“验身份”;二者必须一起重写,且遵守“相等必同哈希”的契约。


















