ARTICLE · 1035534
HashSet 源码分析
HashSet 源码分析
一、HashSet 是什么?
HashSet 是 Java 集合框架中 Set 接口的实现类,它的特点是:
不允许重复元素。无序(不保证元素的顺序)。允许 null 值
底层其实就是包装了一个 HashMap
二、核心源码(带详细注释)
public class HashSet<E> extends AbstractSet<E>implements Set<E>, Cloneable, java.io.Serializable {// ① 底层就是一个 HashMap,key 存元素,value 统一用 PRESENT 占位private transient HashMap<E, Object> map;// ② 定义一个常量 Object,作为 HashMap 的 value 占位符private static final Object PRESENT = new Object();// ③ 无参构造:创建一个空的 HashMappublic HashSet() {map = new HashMap<>();}// ④ 指定初始容量的构造public HashSet(int initialCapacity) {map = new HashMap<>(initialCapacity);}// ⑤ 指定初始容量和负载因子的构造public HashSet(int initialCapacity, float loadFactor) {map = new HashMap<>(initialCapacity, loadFactor);}// ⑥ 根据集合构造 HashSet(把集合元素全部放进去)public HashSet(Collection<? extends E> c) {map = new HashMap<>(Math.max((int) (c.size() / .75f) + 1, 16));addAll(c);}// ⑦ 返回元素个数 → 直接调用 map.size()public int size() {return map.size();}// ⑧ 判断是否为空public boolean isEmpty() {return map.isEmpty();}// ⑨ 判断是否包含某个元素 → 本质是看 map 有没有这个 keypublic boolean contains(Object o) {return map.containsKey(o);}// ⑩ 添加元素:核心方法!public boolean add(E e) {return map.put(e, PRESENT) == null;}// ⑪ 删除元素public boolean remove(Object o) {return map.remove(o) == PRESENT;}// ⑫ 清空所有元素public void clear() {map.clear();}}
四、关键问题解答
1. 为什么 HashSet 不能存重复元素?
因为底层调的是 HashMap.put(key, value),HashMap 的 key 本身就是唯一的。如果 key 已存在,会覆盖旧的 value,但 put() 会返回旧的 value(不是 null),所以 add() 方法返回 false,表示没有"新增"。
2. HashSet 怎么判断两个元素"相同"?
分两步:
先比较 hashCode() → 哈希值不同 → 一定不同
哈希值相同 → 再用 equals() 比较 → 返回 true 才算重复
所以自定义对象存入 HashSet,必须同时重写 hashCode() 和 equals()。
3. 为什么构造函数有 c.size() / .75f + 1
map = new HashMap<>(Math.max((int) (c.size() / .75f) + 1, 16));0.75 是 HashMap 的默认负载因子
这样算出来的容量可以保证放入所有元素后不会触发扩容(避免性能浪费)。最小容量为 16(HashMap 的默认初始容量)