夜雨聆风学习资料网

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();    // ③ 无参构造:创建一个空的 HashMap    public 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) + 116));        addAll(c);    }    // ⑦ 返回元素个数 → 直接调用 map.size()    public int size() {        return map.size();    }    // ⑧ 判断是否为空    public boolean isEmpty() {        return map.isEmpty();    }    // ⑨ 判断是否包含某个元素 → 本质是看 map 有没有这个 key    public 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) + 116));

0.75 是 HashMap 的默认负载因子

这样算出来的容量可以保证放入所有元素后不会触发扩容(避免性能浪费)。最小容量为 16(HashMap 的默认初始容量)

相关学习资料