集合源码四:HashSet

版权声明:欢迎转载,转载请标明来源 https://blog.csdn.net/weixin_41973131/article/details/88951321

一 简介

HashSet可以存放null值,但不允许存在两个相等的元素,后面添加的相同元素会替换掉前者,并且null也只允许存在一个。

由于HashSet的迭代器消耗的时间与HashSet实例元素的大小成正比,再加上capacity容量是依靠HashMap进行实现的,因此在迭代器的效率需要得到重视的情况下,请不要设置太大的初始容量以及太小的加载因子

HashSet并没有实现同步,因此在多条线程操作HashSet并且存在对数据进行修改的情况下,需要在HashSet的外部进行同步处理。

HashSet是利用HashMap进行封装后的产物,定义一个final的Object类,并利用HashMap(key, Object object)以HashMap作为HashSet的基础结构。将增加元素设置为当key相同时不修改该键值对,从而保证了键key的唯一性。并且只针对HashMap中键key的操作构成了HashSet的简单API,并且HashSet的迭代器与可分割迭代器也是使用了HashMap的键迭代器以及键可分割迭代器。

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{

二 成员变量

	// HashMap实例
	private transient HashMap<E,Object> map;

	// 与Map中的对象关联的虚值
    private static final Object PRESENT = new Object();

三 构造器

	// 构建一个空的set
    public HashSet() {
        map = new HashMap<>();
    }

	// 使用指定集合构建一个新的Set,Map实例的加载因子为0.75,容量为16或更高
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

	// 利用指定的初始化容量以及加载因子构建一个新的HashMap
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

	// 构建一个指定容量的HashSet,加载因子默认为0.75
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

	// 构建一个新的LinkedHashSet,这个构造器只能被LinkedHashMap使用,使用指定初始化容量和加载因子构造一个LinkedHashMap,dummy可忽略,只是为了区分于int, float构造器
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

四 构造器

	// 返回该Set集合中元素的迭代器,而该构造器的返回元素是没有特定顺序的
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

五 基本API

	// 返回set中元素的数量
    public int size() {
        return map.size();
    }

	// 如果集合中不存在元素则返回true
    public boolean isEmpty() {
        return map.isEmpty();
    }

	// 如果Set集合中存在指定元素则返回true
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

	// 在Set中不存在指定值的前提下,将e存储到Set中
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

	// 如果Set中包含指定值e,则在Set中移除掉该指定值
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

	// 移除Set中所有的元素
    public void clear() {
        map.clear();
    }

六 克隆

	// 返回一个HashSet的浅赋值
    @SuppressWarnings("unchecked")
    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实例的状态存储到流中(序列化)
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out HashMap capacity and load factor
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // Write out size
        s.writeInt(map.size());

        // Write out all elements in the proper order.
        for (E e : map.keySet())
            s.writeObject(e);
    }

	// 将HashSet实例从流中重新构建(反序列化)
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read capacity and verify non-negative.
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }

        // Read load factor and verify positive and non NaN.
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }

        // Read size and verify non-negative.
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " +
                                             size);
        }
        
        // 设置容量大小
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // 当添加第一个元素时,构造后端映射将惰性地创建一个数组,需要在构造前检查它。利用HashMap的tableSizeof()来计算实际的分配大小。检查Map.Entry[].class,因为它是与实际创建内容最近的公共类型。
        SharedSecrets.getJavaOISAccess()
                     .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));

        // Create backing HashMap
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }

七 可分割迭代器

	// 创建一个可分割迭代器
    public Spliterator<E> spliterator() {
        return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
    }

八 最后

HashSet很大程度上是利用HashMap实现的,其实懂了HashMap的话,HashSet基本没啥理解难度。

猜你喜欢

转载自blog.csdn.net/weixin_41973131/article/details/88951321