HashSet靠hashCode()定位桶、equals()精确判重,自定义类须同时重写二者以保证相等对象哈希值相同;null被特殊处理,仅允许一个;修改影响哈希或判等的字段会导致remove/contains失效。

HashSet 保证元素唯一,靠的是底层用 HashMap 存储,把元素当 key、用一个固定对象(PRESENT)当 value。真正起作用的,是 hashCode() 和 equals() 这两个方法的配合。
哈希值先定位存储位置
调用 add() 时,HashSet 先调用元素的 hashCode() 方法,算出一个整数哈希值。这个值决定它该放在底层哈希表的哪个“桶”里。
- 如果那个桶是空的,直接存进去
- 如果桶里已经有东西,说明发生了哈希冲突——这时候才进入下一步判断
equals() 再做精确比对
只要哈希值相同(即落在同一个桶里),HashSet 就会遍历桶里的每个已有元素,挨个调用 equals() 方法和新元素比较。
- 只要 equals() 返回 true,就认定是重复元素,add() 返回 false,不添加
- 只有所有 equals() 都返回 false,才会真正把新元素加进去
自定义类必须同时重写两个方法
如果你往 HashSet 里放的是自己写的类(比如 Person),默认的 hashCode() 和 equals() 是基于内存地址的,两个内容相同但不同实例的对象会被当成不同元素。
- 只重写 equals() 不行:哈希值不同,压根不会触发 equals() 比较
- 只重写 hashCode() 也不行:哈希值相同但 equals() 仍按默认逻辑返回 false,还是会被当作不同元素
- 必须确保:equals() 返回 true 的两个对象,hashCode() 也必须返回相同值
null 值被特殊处理
HashSet 允许存一个 null 元素。它的实现中对 null 做了单独判断:null 的哈希值被规定为 0,且在 equals 比较时能正确识别 null == null。
- 第二次 add(null) 会被拒绝,size 不变
- 但要注意:如果对象字段含可变状态,插入后修改了影响 hashCode() 或 equals() 的字段,可能导致无法 remove 或 contains 判断失败
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











