前言
书接上文,上一篇中对于 Set 接口与 AbstractSet 抽象实现类做了介绍与分析,本篇将对 Set 最终实现类 HashSet 与 LinkedHashSet 做介绍与分析。
先来看下 HashSet 的源码
/**
* 这个类实现了 Set 接口,有一个 hash table(事实上是一个 HashMap)支持。它对于套的迭代顺序不做保证,此处
* 特指,它不保证顺序能一直保持一致。这个类允许 null 元素,结社 hash 功能正确地在桶中分散了元素。
*
* 这个类为基本操作(add,remove,contains 和 size)提供了与事件一直的表现,迭代这个套锁需要的时间与
* HashSet 实例的长度(元素数量)加上支持的 HashMap 实例的“容量”(桶个数)成正比。因此,如果迭代性能很重要
* 的话,不将初始容量设置的太高(或者加载因子太低)就是很重要的。
*
* 注意这个实现类不是线程安全的。如果多线程并发地访问一个 hash set,并且至少有一个线程更改了 set,它必须从
* 外部实现线程安全。这通常通过对封装了 set 的对象实现线程安全来完成。
*
* 如果没有类似的对象存在,set 应该使用 Collections#synchronizedSet 方法“包装”。这最好在创建的时候完
* 成,来防止意外的对于 set 的非线程安全的访问: <pre>
* Set s = Collections.synchronizedSet(new HashSet(...));</pre>
*
* 这个类的 iterator 方法返回的迭代器是 fail-fast 的:如果 set 在被创建后的任何时间点被更改了,以除了通过
* 迭代器自己的 remove 方法的其他任何方式,迭代器会抛出一个 ConcurrentModificationException。因此,在
* 并发修改操作时,迭代器失败地快速和干净,而不是在未来某个不确定的时间点,冒险做武断的,描述不清的动作。
*
* 注意一个迭代器的 fail-fast 行为是不能被保证的,通常来说,不可能对出现的非线程安全的同时修改操作做任何硬性
*的保证。基于最佳性能的基础考虑,Fail-fast 迭代器抛出一个 ConcurrentModificationException。因此,建立
* 在这中异常上写出的程序的正确性将会是不健壮的:迭代器的 fail-fast 行为应当植被用于检查 bugs。
*
* HashSet 继承自 AbstractSet 抽象类,实现了 Set 接口,Cloneable 接口以及 Serializable 接口。
*/
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
/**不参与序列化的部分**/
private transient HashMap<E,Object> map;
/**与一个在支持 map 中的对象相关的壳值**/
private static final Object PRESENT = new Object();
/**
* 构造一个新的,空的 set,支持的 HashMap 实例有模式容量(16)和加载因子(0.75)
*/
public HashSet() {
map = new HashMap<>(); //构造一个空 HashMap
}
/**
* 构造一个新的包含指定数据结构中的元素的 set,HashMap 被使用默认加载因子(0.75)和一个足够包含指定数
* 据结构中元素的初始化容量来构造。
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); //传参构造一个空 HashMap
addAll(c); //调用 addAll 方法
}
/**
* 构造一个新的,空的 set,支持 HashMap 实例有指定的初始容量和指定的加载因子
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor); //传参构造一个空的 HashMap
}
/**
* 构造一个新的,空的 set,支持 HashMap 实例有指定的初始容量和模式的加载因子(0.75)。
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity)