概述
HashSet 是 Set 接口下一个不允许重复但允许 null、无序并且线程不安全的集合。它基于 HashMap 实现。
从数据结构来说,他与 HashMap 相同,但是由于 HashSet 借助 HashMap 的 key 来存储数据,因而 HashMap 的 value 在 HashSet 中无意义。
1.数据结构
HashSet 基于 HashMap 实现,也就是说,HashSet 用于存储数据的容器实际上就是一个 HashMap 实例。(关于 HashMap 的数据结构,可以参考前文java集合源码分析(六):HashMap中的第一部分。)
HashSet 使用 HashMap 的 key 作为存储数据的位置,而 value 的位置使用一个默认的全局空对象填充。大部分方法通过直接包装调用 HashMap 的来实现,也就是说,我们可以把 HashSet 看成 HashMap 的一个大号包装器——或者说适配器类。
2.成员变量
由于直接基于 HashMap 实现,因而 HashSet 中只提供两个成员变量,一个 map 用于存放 HashMap 实例,一个 PRESENT 用于作为填充 value 的空对象。
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
3.构造方法
HashSet 提供了五个构造方法:
public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
没什么好说的,比较值得注意的是直接传入 Collection 集合的构造方法,这个方法取 HashMap 的默认容量16与传入容量除以默认负载系数的最大值,避免插入过程发生一次额外的扩容。
4.成员方法
Set 接口继承了 Collection 接口,但是 Set 接口并未提供一些新的抽象方法。因而 HashSet 内部的方法基本都重写自 AbstractCollection 抽象类,并且实现都是基于 HashMap 方法的再包装。
也正由于此,HashSet 集合元素允许 null 但是不允许重复,因为 HashMap 的 key 允许有一个 null,并且不允许重复。
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
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();
}
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
总结
HashSet 基于 HashMap 实现,并且使用 HashMap 的 key 作为存储容器,将对应的 value 以一个 Object 对象常量进行充填。
HashSet 中的元素无序、不允许重复、允许一个 null 的特性皆来源于 HashMap 的 key 的特性。
实际上,HashSet 中的方法都来自于 Collection 接口,而真正的实现都来自于 HashMap,从这个角度来看,HashSet 其实就是 HashMap 与 Collection 接口之间的一种适配器。