HashSet源码解析。

版权声明:转载需说明出处。 https://blog.csdn.net/en_joker/article/details/89558197
/**
 * 此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;
 * 特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
 * 此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size,假定哈希函数将这些元素正确地分布在桶中。
 * 对此 set 进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。
 * 因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
 * 注意,此实现不是同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。
 * 这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。
 * 最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
 * Set s = Collections.synchronizedSet(new HashSet(...));
 * 此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果对 set 进行修改,除非通过迭代器自身的 remove 方法,
 * 否则在任何时间以任何方式对其进行修改,Iterator 都将抛出 ConcurrentModificationException。
 * 因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来在某个不确定时间发生任意不确定行为的风险。
 * 注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。
 * 快速失败迭代器在尽最大努力抛出 ConcurrentModificationException。
 * 因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误做法:迭代器的快速失败行为应该仅用于检测 bug。
 */
public class HashSet<E> extends AbstractSet implements Set<E>, Cloneable, java.io.Serializable {
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;
    // 与支持映射中的对象关联的虚值
    private static final Object PRESENT = new Object();

    /**
     * 构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * 构造一个包含指定 collection 中的元素的新 set。
     * 使用默认的加载因子 0.75 和足以包含指定 collection 中所有元素的初始容量来创建 HashMap。
     * @param c 其中的元素将存放在此 set 中的 collection
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c); // 将c中所有元素都添加到此 map中。
    }

    /**
     * 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。
     * @param initialCapacity   哈希映射的初始容量
     * @param loadFactor    哈希映射的加载因子
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
     * 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
     * @param initialCapacity   哈希表的初始容量
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * 构造一个新的空链接哈希集(这个包的私有构造函数只被LinkedHashSet使用)。
     * 支持的HashMap实例是一个LinkedHashMap,具有指定的初始容量和指定的负载因子。
     * @param initialCapacity   哈希表的初始容量
     * @param loadFactor    哈希映射的加载因子
     * @param dummy 忽略(将此构造函数与其他int、float构造函数区分开来)。
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    /**
     * 返回对此 set 中元素进行迭代的迭代器。
     * 返回元素的顺序并不是特定的。
     * 实现:类 AbstractCollection<E> 中的 iterator
     * @return 对此 set 中元素进行迭代的 Iterator
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    /**
     * 返回此 set 中的元素的数量(set 的容量)。
     * 实现:类 AbstractCollection<E> 中的 size
     * @return  此 set 中的元素的数量(set 的容量)
     */
    public int size() {
        return map.size();
    }

    /**
     * 如果此 set 不包含任何元素,则返回 true。
     * 实现:类 AbstractCollection<E> 中的 isEmpty
     * @return 如果此 set 不包含任何元素,则返回 true
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * 如果此 set 包含指定元素,则返回 true。 更确切地讲,
     * 当且仅当此 set 包含一个满足 (o==null ? e==null : o.equals(e)) 的 e 元素时,返回 true。
     * 实现:类 AbstractCollection<E> 中的 contains
     * @param o 其在此 set 中的存在已得到测试的元素
     * @return  如果此 set 包含指定元素,则返回 true
     */
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    /**
     * 如果此 set 中尚未包含指定元素,则添加指定元素。更确切地讲,
     * 如果此 set 没有包含满足 (e==null ? e2==null : e.equals(e2)) 的元素 e2,则向此 set 添加指定的元素 e。
     * 如果此 set 已包含该元素,则该调用不更改 set 并返回 false。
     * 实现:类 AbstractCollection<E> 中的 add
     * @param e 将添加到此 set 中的元素
     * @return 如果此 set 尚未包含指定元素,则返回 true
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    /**
     * 如果指定元素存在于此 set 中,则将其移除。更确切地讲,
     * 如果此 set 包含一个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则将其移除。
     * 如果此 set 已包含该元素,则返回 true(或者:如果此 set 因调用而发生更改,则返回 true)。
     * (一旦调用返回,则此 set 不再包含该元素)。
     * 实现:类 AbstractCollection<E> 中的 remove
     * @param o 如果存在于此 set 中则需要将其移除的对象
     * @return 如果 set 包含指定元素,则返回 true
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    /**
     * 从此 set 中移除所有元素。此调用返回后,该 set 将为空。
     * 实现:类 AbstractCollection<E> 中的 clear
     */
    public void clear() {
        map.clear();
    }

    /**
     * 返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。
     * 实现:类 Object 中的 clone
     * @return 此 set 的浅表副本
     */
    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();
        }
    }

    /**
     * 将这个HashSet实例的状态保存到一个流中(即序列化它)。
     * @serialData  返回HashMap实例(int)的容量及其负载因子(float)被发出,
     *      后面跟着集合的大小(它包含的元素的数量)(int),
     *      后面跟着没有特定顺序的所有元素(每个对象)。
     * @param s
     * @throws java.io.IOException
     */
    private void writeObject(java.io.ObjectOutputStream s)
            throws java.io.IOException {
        // 写出任何隐藏的序列化魔法
        s.defaultWriteObject();

        // 写出HashMap容量和负载因子
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // 写出大小
        s.writeInt(map.size());

        // 按正确的顺序写出所有的元素。
        for (E e : map.keySet())
            s.writeObject(e);
    }

    /**
     *  从流(即反序列化)中重新构造HashSet实例。
     * @param s
     * @throws java.io.IOException
     * @throws ClassNotFoundException
     */
    private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
        // 读取任何隐藏的序列化魔法
        s.defaultReadObject();

        // 读取HashMap容量和负载因子,并创建支持的HashMap
        int capacity = s.readInt();
        float loadFactor = s.readFloat();
        map = (((HashSet)this) instanceof LinkedHashSet ?
                new LinkedHashMap<E,Object>(capacity, loadFactor) :
                new HashMap<E,Object>(capacity, loadFactor));

        // 读取大小
        int size = s.readInt();

        // 按适当的顺序读取所有元素。
        for (int i=0; i<size; i++) {
            E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/en_joker/article/details/89558197