HashSet判断元素存在依赖hashCode()定位桶、equals()确认相等,自定义类须重写二者且逻辑一致;null被特殊处理,平均时间复杂度O(1)。

HashSet 判断元素是否存在,靠的是 hashCode() + equals() 两个方法协同工作,不是简单比内存地址,也不是只看哈希值。
底层原理:先算哈希桶,再逐个比较
当你调用 set.contains(obj) 时,HashSet 会:
- 先调用
obj.hashCode(),算出哈希值,再对数组长度取模,定位到对应的“桶”(数组索引) - 如果该桶为空,直接返回
false - 如果该桶有元素(可能是单个对象,也可能是链表或红黑树),就遍历其中每个元素,对每个元素调用
obj.equals(已存元素) - 只要有一次
equals()返回true,就认为存在,立即返回true
关键前提:自定义类必须重写 hashCode 和 equals
如果用的是 String、Integer 等 JDK 内置类型,不用操心——它们已经正确实现了这两个方法。但如果你用的是自己写的类,比如:
class Person { String name; int age; }
那必须手动重写 hashCode() 和 equals(Object o),否则:
立即学习“Java免费学习笔记(深入)”;
- 不同实例的
hashCode()很可能不同 → 被分到不同桶 →contains()找不到,即使内容一样 - 默认
equals()比的是引用 → 即使 name 和 age 完全相同,也会返回false
IDE(如 IntelliJ)通常支持一键生成这两个方法,确保逻辑一致:两个对象 equals() 为 true,则它们的 hashCode() 必须相等。
注意 null 值的特殊处理
HashSet 允许存一个 null 元素。判断 null 是否存在时:
- 不调用
null.hashCode()(会空指针),而是专门处理:哈希值固定为 0,且用== null判断相等性 - 所以
set.contains(null)是安全的,只要集合里真加过null,就会返回true
时间复杂度:平均 O(1),最坏 O(n)
理想情况下,哈希分布均匀、无大量冲突,一次定位+一次 equals 就能出结果;但如果多个对象哈希值撞到同一个桶,又没转成红黑树(JDK 8+ 中链表长度 ≥8 且数组长度 ≥64 才树化),就得顺序遍历链表,退化成 O(n)。


















