hashset通过内部hashmap的key唯一性实现去重,add(e)实际调用map.put(e, present),重复判断依赖hashcode()和equals()一致重写;null仅允许一个。

HashSet 本身不负责去重,它只是把元素原封不动地塞进内部 HashMap 的 key 位置,靠 HashMap 的 key 不允许重复这一特性来实现唯一性。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
底层就是 HashMap 的一个“包装”
HashSet 内部持有一个 HashMap 实例,每次调用 add(e),实际执行的是:
map.put(e, PRESENT)
其中 PRESENT 是一个静态的、无意义的 Object 占位对象。value 本身不参与去重判断,全靠 key 的唯一约束。
重复判断完全由 HashMap 的 put 逻辑完成
- 先根据元素的 hashCode() 计算哈希值,定位到数组中的某个桶(bucket)
- 如果该桶为空,直接插入新节点
- 如果桶里已有节点,就逐个比对:
→ 先看 hash 值是否相等
→ hash 相等再调用 equals() 判断是否真正相同 - 只要找到一个 key.equals(e) 为 true 的节点,就认为重复,不新增,也不报错,只返回 false
自定义类必须同时重写 hashCode() 和 equals()
这两个方法必须保持一致:逻辑上相等的对象,hashCode 必须相同;hashCode 不同,equals 就不能返回 true。
否则会出现两种典型问题:
- 只重写 hashCode():不同对象可能落到同一桶,但因 equals 返回 false,仍被当作不同元素
- 只重写 equals():相同对象因默认 hashCode 基于地址,散列到不同桶,HashMap 永远不会触发 equals 比较,导致重复添加
null 元素的特殊处理
HashMap 允许一个 null key,所以 HashSet 也只允许存一个 null。它的哈希值被约定为 0,且 null.equals(null) 为 true,因此第二次 add(null) 会被识别为重复。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










