java集合(三)Set集合之HashSet詳解

 

①:實現了Serializable接口,代表它支持序列化。
②:實現了Cloneable接口,代表它支持克隆,能夠調用超類的clone()方法進行淺拷貝。
③繼承了AbstractSet抽象類,和ArrayList和LinkedList同樣,在他們的抽象父類中,都提供了equals()方法和hashCode()方法。它們自身並不實現這兩個方法,(可是ArrayList和LinkedList的equals()實現不一樣。你能夠看個人關於ArrayList這一塊的源碼解析)這就意味着諸如和HashSet同樣繼承自AbstractSet抽象類的TreeSet、LinkedHashSet等,他們只要元素的個數和集合中元素相同,即便他們是AbstractSet不一樣的子類,他們equals()相互比較的後的結果仍然是true。下面給出的代碼是JDK中的equals()代碼:
從JDK源碼能夠看出,底層並無使用咱們常規認爲的利用hashcode()方法求的值進行比較,而是經過調用AbstractCollection的containsAll()方法,若是他們中元素徹底相同(與順序無關),則他們的equals()方法的比較結果就爲true。java

public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Set)) return false; Collection<?> c = (Collection<?>) o; //必須保證元素的個數相等。
        if (c.size() != size()) return false; try { //調用了AbstractCollection的方法。
            return containsAll(c); } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } }
public boolean containsAll(Collection<?> c) { //只須要逐個判斷集合是否包含其中的元素。
        for (Object e : c) if (!contains(e)) return false; return true; }

④實現了Set接口。數組

老規矩仍是先把總結放到上面:
①HashSet內部經過使用HashMap的鍵來存儲集合中的元素,並且內部的HashMap的全部值
都是null。(由於在爲HashSet添加元素的時候,內部HashMap的值都是PRESENT),而PRESENT在實例域的地方直接初始化了,並且不容許改變。
②HashSet對外提供的全部方法內部都是經過HashMap操做完成的,因此,要真正理解HashSet的實現,只須要把HashMap的原理理解便可。我也對HashMap作了分析。傳送門:
③因此,若是你對HashMap很熟悉的話,學習HastSet的源碼垂手可得!數據結構

1:首先咱們先看看HashSet的有哪些實例域:(是否是少了不少,並且還簡單(⊙o⊙)…)學習

//序列化ID
    static final long serialVersionUID = -5024744406713321676L; //底層使用了HashMap存儲數據。
    private transient HashMap<E,Object> map; //用來填充底層數據結構HashMap中的value,由於HashSet只用key存儲數據。
    private static final Object PRESENT = new Object();

2:老規矩,仍是先看一下它們的構造方法:
構造器的實現基本都是使用HashMapthis

//其實只是實例化了HashMap (⊙o⊙)…(不懂的童鞋,能夠看個人另外一篇關於HashMap的源碼解析)
    public HashSet() { map = new HashMap<>(); }
//仍是關於
    public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } //調用的AbstractCollection中的方法。而後調用HashSet的add()方法把集合中的 //全部元素添加到了集合中。(由於)
    public boolean addAll(Collection<? extends E> c) { boolean modified = false; for (E e : c) if (add(e)) modified = true; return modified; } //底層並不支持直接在AbstractCollection類中調用add()方法,而是調用add()方法 //的自身實現。
public boolean add(E e) { throw new UnsupportedOperationException(); }
//初始化HashSet仍然是關於HashMap的知識。
    public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); }
//初始化HashSet仍然是關於HashMap的知識。
    public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); }
//此次初始化底層使用了LinkedHashMap實現。
    HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }

3:添加新的元素:spa

//底層仍然利用了HashMap鍵進行了元素的添加。 //在HashMap的put()方法中,該方法的返回值是對應HashMap中鍵值對中的值,而值老是PRESENT, //該PRESENT一直都是private static final Object PRESENT = new Object(); //PRESENT只是初始化了,並不能改變,因此PRESENT的值一直爲null。 //因此只要插入成功了,put()方法返回的值老是null。
    public boolean add(E e) { return map.put(e, PRESENT)==null; }

4:刪除一個元素:code

 

//該方法底層實現了仍然使用了map的remove()方法。 //map的remove()方法的返回的是被刪除鍵對應的值。(在HashSet的底層HashMap中的全部 //鍵值對的值都是PRESENT)
    public boolean remove(Object o) { return map.remove(o)==PRESENT; }

5:清空方法對象

public void clear() { map.clear(); }

6:克隆方法(淺克隆)
底層仍然使用了Object的clone()方法,獲得的Object對象,並把它強制轉化爲HashSet<E>,而後把它的底層的HashMap也克隆一份(調用的HashMap的clone()方法),並把它賦值給newSet,最後返回newSet便可。blog

@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); } }

7:是否包含某個元素繼承

底層仍然使用了HashMap的containsKey()方法(由於HashSet只用到了HashMap的鍵)一樣的話重複了好多遍 ……….(艹皿艹 )

public boolean contains(Object o) { return map.containsKey(o); }

8:判斷是否爲空
仍然調用HashMap的方法。

public boolean isEmpty() { return map.isEmpty(); }

9:生成一個迭代器

仍然使用了HashMap的鍵的迭代器

public Iterator<E> iterator() { return map.keySet().iterator(); }

10: 統計HashSet中包含元素的個數

仍是調用HashMap的實現。

public int size() { return map.size(); }

11:把HashSet寫入流中(也就是序列化)
在HashSet序列化實現中,仍然是把HashMap中的屬性寫入流中。(由於HashSet中真正存儲數據的數據結構是HashMap。)

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 //把HashMap的容量寫入流中。
 s.writeInt(map.capacity()); //把HashMap的裝載因子寫入流中。
 s.writeFloat(map.loadFactor()); //把HashMap中鍵值對的個數寫入流中。
 s.writeInt(map.size()); // 按正確的順序把集合中的全部元素寫入流中。
        for (E e : map.keySet()) s.writeObject(e); }

12:從流中讀取數據,組裝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); } // 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中構建哈希桶數組是在第一個元素被添加的時候才構建,因此在構建以前檢查它, // 調用HashMap.tableSizeFor來計算實際分配的大小, // 檢查Map.Entry []類,由於它是最接近的公共類型實際建立的內容。
 SharedSecrets.getJavaOISAccess() .checkArray(s, Map.Entry[].class,HashMap.tableSizeFor(capacity)); //建立HashMap。
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); // 按寫入流中的順序再把元素依次讀取出來放到map中。
        for (int i=0; i<size; i++) { @SuppressWarnings("unchecked") E e = (E) s.readObject(); map.put(e, PRESENT); } }
相關文章
相關標籤/搜索