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使用呢!

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