hashset本质是hashmap的键集合,内部用hashmap存储元素,key为元素本身、value为共享的present对象;唯一性由hashmap的key约束保证,add操作依赖put返回值判断是否新增;定位和判等需hashcode()与equals()协同,自定义类必须一致重写二者。

HashSet 本质是 HashMap 的键集合
HashSet 自身不直接存数据,它内部持有一个 HashMap 实例,所有添加的元素都作为这个 HashMap 的 key,而 value 统一设为一个共享的静态对象 PRESENT(类型是 Object)。所以调用 set.add(e),实际执行的是 map.put(e, PRESENT)。
唯一性由 HashMap 的 key 约束自然实现
HashMap 明确规定:同一个 key 只能存在一份。当 put 一个已存在的 key 时,新 value 会覆盖旧 value,但 key 不会重复插入,且 put() 方法会返回原 value(即 PRESENT);如果是新 key,则返回 null。HashSet 正是依据这个返回值判断 add 是否成功——返回 null 表示新增,否则视为重复并忽略。
定位与判等依赖 hashCode() 和 equals()
元素能否被正确定位和识别为“重复”,取决于两个方法协同工作:
- hashCode() 决定元素该放进哪个桶(数组索引),影响初始查找效率
- 若发生哈希冲突(多个对象落在同一桶),则遍历该桶内已有元素,逐个调用 equals() 判断是否真正相等
- 只有
hashCode() 相同 && equals() 返回 true,才认定为重复元素
自定义类必须重写两者且逻辑一致
如果用自定义对象作 HashSet 元素,不重写 hashCode() 和 equals(),就会沿用 Object 默认实现——后者基于内存地址,导致内容相同但地址不同的对象被当成不同元素;前者默认也基于地址,无法保证相等对象有相同哈希值。结果就是:
- 本该去重的没去重(重复添加)
- 已添加的元素后续
contains()查不到(哈希位置错、equals 不匹配) - 修改对象状态后,可能再也找不回它(哈希值变了,原位置找不到,新位置又没存)










