Java基础之Collections框架Set实现类HashSet及其源码分析

Java基础之Collections框架Set实现类HashSet及其源码分析


这个类实现了Set接口,由一个哈希表(实际上是一个HashMap实例,HashMap研究到了再说,哈哈)支持。它不能保证集合的迭代顺序;特别是,它不能保证顺序随时间保持不变。这个类允许空元素。
这个类为基本操作(添加、删除、包含和大小)提供恒定的时间性能,假设哈希函数将元素正确地分散在bucket中。遍历这个集合所需的时间与HashSet实例的大小(元素的数量)与支持HashMap实例的“容量”(桶的数量)的总和成正比。因此,如果迭代性能很重要,那么不要将初始容量设置得太高(或负载系数设置得太低),这一点非常重要。

HashSet简单使用

Set<String> example = new Hash<>();
//添加元素
example.add("tony");
example.add("project");
//移除元素
example.remove("tony");
//包含
example.contains("project");
//判断为空
example.isEmpty();
还有很多方法,我们来看看HastSet的源码

HashSet源码分析

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

{
//序列版本编号
static final long serialVersionUID = -5024744406713321676L;
//HashMap对象
private transient HashMap<E,Object> map;

    // 伪值,以便与后备映射中的对象相关联
    private static final Object PRESENT = new Object();

    /**
     HashSet的默认构造函数,创建一个HashMap,默认大小为16,扩容因子为0.75
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * 构造一个包含指定元素的新集合,默认大小为16,扩容因子为0.75
     * 如果集合的大小为12的时候,初始化的大小为17,进行了一次扩容,如果小于12的话,会返回16
     */
    public HashSet(Collection<? extends E> c) {
    	//当c.size为12的时候,Math.max(17,16)  返回17  max((int)(12 /0.75) + 1,16)
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    /**
   创建一个hashSet,指定初始容量和扩容因子
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
   创建一个hastSet,指定初始容量,使用默认的扩容因子 0.75
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * 创建一个新的,空的 LinkedHashSet ,后台HashMap实例是一个LinkedHashMap,具有指定的初始容量和指定的扩容因子。
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    /**
    返回相应当前结合的迭代器,是使用HashMap中的,可以看看hashMap的相关源码
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    /**
     返回hashSet集合中的元素个数
     */
    public int size() {
        return map.size();
    }

    /**
    返回set集合是否为空
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
   返回set集合中是否包含指定的元素
     */
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    /**
     * 如果指定的元素不存在,则将其添加到此集合中
     * 如果存在将返回false
     */
    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();
    }

    /**
    返回一个浅复制的HashSet
     */
    @SuppressWarnings("unchecked")
    public Object clone() {
        try {
        	//这里直接copy的是hashMap中的值
            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实例的容量大小和扩容因子
   后面是集合的大小(包含的元素数量) (int),后面是集合的所有元素(每个都是对象),没有特定的顺序。
     */
    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实例
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // 读取任何隐藏的序列化魔法
        s.defaultReadObject();

        // 读取容量并验证非负性
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }

        // 读取负载因数并验证正和非NaN
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }

        // 读取集合的大小并验证非负性
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " +
                                             size);
        }

        // Set the capacity according to the size and load factor ensuring that
        // the HashMap is at least 25% full but clamping to maximum capacity.
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // 创建一个HashMap或者LinkedHashMap
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        //读取所有的元素
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }

    /**
    创建一个Spliterator
     */
    public Spliterator<E> spliterator() {
        return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
    }
}

从上面的源码可以看出,操作HashSet就是操作HashMap中的key,key对应的都是一个空的object对象。Object PRESENT = new Object(); 因为HashMap中的key是不能重复,是不是就可以作为HashSet使用呢!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章