前言
HashSet 是 Java 集合框架中的一种无序、不重复的集合实现,属于 java.util 包。它实现了 Set 接口,底层基于 HashMap 实现。HashSet 的主要特点:
- 无序性:元素存储顺序与插入顺序无关。
- 唯一性:不允许重复元素(通过
equals()和hashCode()判断)。 - 高效性:增删查操作的平均时间复杂度为 $O(1)$。
- 允许null:可以包含一个null元素。
源码分析
数据结构
HashSet 的实现依赖于 HashMap,具体来说,HashSet 的元素作为 HashMap 的键存储,而值则是一个固定的占位对象。
构造方法
HashSet 的五个构造方法如下,它们都围绕底层 HashMap 的初始化展开:
HashSet()- 默认构造方法HashSet(int initialCapacity)- 指定初始容量HashSet(int initialCapacity, float loadFactor)- 指定初始容量和加载因子HashSet(Collection<? extends E> c)- 从另一个集合初始化HashSet(int initialCapacity, float loadFactor, boolean dummy)- 内部使用的特殊构造方法(不公开)
1 | public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable { |
除了第四个构造方法外,其他构造方法传入的 initialCapacity 都会被 HashMap 调整为 2 的幂。
第四个构造方法细节:
- 容量计算:
Math.max((int) (c.size() / .75f) + 1, 16)。c.size() / 0.75f:根据集合大小和默认加载因子0.75估算所需容量。+1:确保容量足够。Math.max(..., 16):保证容量至少为16。
addAll(c):调用AbstractCollection的addAll,逐个添加元素到HashMap中。
第五个构造方法是为 LinkedHashSet 设计的,体现了 Java 集合框架的复用性。
核心方法分析
在 HashSet 中,最为核心的是添加、删除这两个方法。
添加元素 - add(E e)
1 | public boolean add(E e) { |
add方法通过调用HashMap.put将元素作为键插入。HashMap.put返回的是插入前的旧值。如果返回null,说明之前没有这个键,插入成功,返回true;否则返回旧值(PRESENT),表示元素已存在,返回false。
去重逻辑:依赖 HashMap 的键唯一性,内部会调用元素的 hashCode() 和 equals() 方法判断是否重复。
删除元素 - remove(Object o)
1 | public boolean remove(Object o) { |
- 调用
HashMap.remove,如果成功移除(即返回PRESENT),则返回true。 - 删除操作依赖
HashMap的哈希查找,时间复杂度 $O(1)$。
后记
HashSet 是非线程安全的,多线程环境下需要外部同步(如 Collections.synchronizedSet 或 ConcurrentHashMap)。
HashSet 本质上是 HashMap 的封装,元素存储在 HashMap 的键中,值统一为PRESENT。