Java數據結構篇七:HashSet詳解

HashSet介紹

HashSet繼承自AbstractSet,HashSet基於HashMap實現的,HashSet 底層使用HashMap 來保存所有元素。將HashSet的數據作爲HashMap的Key值保存,利用HashMap的key不可重複從而實現HashSet不含重複元素。所以 ①HashSet中元素不可重複 ②元素存儲無序,而且HashSet允許使用 null 元素(僅一個)③線程不安全,Collections.synchronizedSet(new HashSet(…))確保安全;

構造方法

構造一個新的空 set,其底層 HashMap 實例的默認初始容量是 16,加載因子是 0.75HashSet()
構造一個包含指定 collection 中的元素的新 set。
HashSet(Collection<? extends E> c)
構造一個新的空 set,其底層 HashMap 實例具有指定的初始容量和默認的加載因子(0.75)。
HashSet(int initialCapacity)
構造一個新的空 set,其底層 HashMap 實例具有指定的初始容量和指定的加載因子。   
HashSet(int initialCapacity, float loadFactor)        

常用方法

返回此 set 中的元素的數量(set 的容量)。
size()  
        
如果此 set 不包含任何元素,則返回 trueisEmpty()

如果此 set 中尚未包含指定元素,則添加指定元素。         
add(E e)

返回對此 set 中元素進行迭代的迭代器。         
iterator()
        
如果指定元素存在於此 set 中,則將其移除。
remove(Object o)

如果此 set 包含指定元素,則返回 truecontains(Object o)
         
從此 set 中移除所有元素。
clear()        

HashSet的實現原理總結:

①是基於HashMap實現的,默認構造函數是構建一個初始容量爲16,負載因子爲0.75 的HashMap。封裝了一個 HashMap 對象來存儲所有的集合元素,所有放入 HashSet 中的集合元素實際上由 HashMap 的 key 來保存,而 HashMap 的 value 則存儲了一個 PRESENT,它是一個靜態的 Object 對象。

②當我們試圖把某個類的對象當成 HashMap的 key,或試圖將這個類的對象放入 HashSet 中保存時,重寫該類的equals(Object obj)方法和hashCode() 方法很重要,而且這兩個方法的返回值必須保持一致:當該類的兩個的 hashCode() 返回值相同時,它們通過 equals() 方法比較也應該返回 true。通常來說,所有參與計算 hashCode() 返回值的關鍵屬性,都應該用於作爲 equals() 比較的標準。

③HashSet的其他操作都是基於HashMap的。

源碼解析

public class HashSet<E>  
    extends AbstractSet<E>  
    implements Set<E>, Cloneable, java.io.Serializable  
{  
    static final long serialVersionUID = -5024744406713321676L;  
  
    // 底層使用HashMap來保存HashSet中所有元素。  
    private transient HashMap<E,Object> map;  
      
    // 定義一個虛擬的Object對象作爲HashMap的value,將此對象定義爲static final。  
    private static final Object PRESENT = new Object();  
  
    /** 
     * 默認的無參構造器,構造一個空的HashSet。 
     *  
     * 實際底層會初始化一個空的HashMap,並使用默認初始容量爲16和加載因子0.75。 
     */  
    public HashSet() {  
    map = new HashMap<E,Object>();  
    }  
  
    /** 
     * 構造一個包含指定collection中的元素的新set。 
     * 
     * 實際底層使用默認的加載因子0.75和足以包含指定 
     * collection中所有元素的初始容量來創建一個HashMap。 
     * @param c 其中的元素將存放在此set中的collection。 
     */  
    public HashSet(Collection<? extends E> c) {  
    map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));  
    addAll(c);  
    }  
  
    /** 
     * 以指定的initialCapacity和loadFactor構造一個空的HashSet。 
     * 
     * 實際底層以相應的參數構造一個空的HashMap。 
     * @param initialCapacity 初始容量。 
     * @param loadFactor 加載因子。 
     */  
    public HashSet(int initialCapacity, float loadFactor) {  
    map = new HashMap<E,Object>(initialCapacity, loadFactor);  
    }  
  
    /** 
     * 以指定的initialCapacity構造一個空的HashSet。 
     * 
     * 實際底層以相應的參數及加載因子loadFactor爲0.75構造一個空的HashMap。 
     * @param initialCapacity 初始容量。 
     */  
    public HashSet(int initialCapacity) {  
    map = new HashMap<E,Object>(initialCapacity);  
    }  
  
    /** 
     * 以指定的initialCapacity和loadFactor構造一個新的空鏈接哈希集合。 
     * 此構造函數爲包訪問權限,不對外公開,實際只是是對LinkedHashSet的支持。 
     * 
     * 實際底層會以指定的參數構造一個空LinkedHashMap實例來實現。 
     * @param initialCapacity 初始容量。 
     * @param loadFactor 加載因子。 
     * @param dummy 標記。 
     */  
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {  
    map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);  
    }  
  
    /** 
     * 返回對此set中元素進行迭代的迭代器。返回元素的順序並不是特定的。 
     *  
     * 底層實際調用底層HashMap的keySet來返回所有的key。 
     * 可見HashSet中的元素,只是存放在了底層HashMap的key上, 
     * value使用一個static final的Object對象標識。 
     * @return 對此set中元素進行迭代的Iterator。 
     */  
    public Iterator<E> iterator() {  
    return map.keySet().iterator();  
    }  
  
    /** 
     * 返回此set中的元素的數量(set的容量)。 
     * 
     * 底層實際調用HashMap的size()方法返回Entry的數量,就得到該Set中元素的個數。 
     * @return 此set中的元素的數量(set的容量)。 
     */  
    public int size() {  
    return map.size();  
    }  
  
    /** 
     * 如果此set不包含任何元素,則返回true。 
     * 
     * 底層實際調用HashMap的isEmpty()判斷該HashSet是否爲空。 
     * @return 如果此set不包含任何元素,則返回true。 
     */  
    public boolean isEmpty() {  
    return map.isEmpty();  
    }  
  
    /** 
     * 如果此set包含指定元素,則返回true。 
     * 更確切地講,當且僅當此set包含一個滿足(o==null ? e==null : o.equals(e)) 
     * 的e元素時,返回true。 
     * 
     * 底層實際調用HashMap的containsKey判斷是否包含指定key。 
     * @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。 
     * 
     * 底層實際將將該元素作爲key放入HashMap。 
     * 由於HashMap的put()方法添加key-value對時,當新放入HashMap的Entry中key 
     * 與集合中原有Entry的key相同(hashCode()返回值相等,通過equals比較也返回true), 
     * 新添加的Entry的value會將覆蓋原來Entry的value,但key不會有任何改變, 
     * 因此如果向HashSet中添加一個已經存在的元素時,新添加的集合元素將不會被放入HashMap中, 
     * 原來的元素也不會有任何改變,這也就滿足了Set中元素不重複的特性。 
     * @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不再包含該元素)。 
     * 
     * 底層實際調用HashMap的remove方法刪除指定Entry。 
     * @param o 如果存在於此set中則需要將其移除的對象。 
     * @return 如果set包含指定元素,則返回true。 
     */  
    public boolean remove(Object o) {  
    return map.remove(o)==PRESENT;  
    }  
  
    /** 
     * 從此set中移除所有元素。此調用返回後,該set將爲空。 
     * 
     * 底層實際調用HashMap的clear方法清空Entry中所有元素。 
     */  
    public void clear() {  
    map.clear();  
    }  
  
    /** 
     * 返回此HashSet實例的淺表副本:並沒有複製這些元素本身。 
     * 
     * 底層實際調用HashMap的clone()方法,獲取HashMap的淺表副本,並設置到HashSet中。 
     */  
    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();  
        }  
    }  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章