HashSet源碼分析

轉自;

HashSet 是一個沒有重複元素的集合
它是由HashMap實現的,不保證元素的順序,而且HashSet允許使用 null 元素
HashSet非同步的。如果多個線程同時訪問一個哈希 set,而其中至少一個線程修改了該 set,那麼它必須 保持外部同步。這通常是通過對自然封裝該 set 的對象執行同步操作來完成的。如果不存在這樣的對象,則應該使用 Collections.synchronizedSet 方法來“包裝” set。最好在創建時完成這一操作,以防止對該 set 進行意外的不同步訪問:

  1. Set s = Collections.synchronizedSet(new HashSet(...)); 

HashSet通過iterator()返回的迭代器是fail-fast的。


  1. public class HashSet<E>  
  2.     extends AbstractSet<E>  
  3.     implements Set<E>, Cloneable, java.io.Serializable  
  4. {  
  5.     static final long serialVersionUID = -5024744406713321676L;  
  6.  
  7.     // HashSet是通過map(HashMap對象)保存內容的  
  8.     private transient HashMap<E,Object> map;  
  9.  
  10.     // PRESENT是向map中插入key-value對應的value  
  11.     // 因爲HashSet中只需要用到key,而HashMap是key-value鍵值對;  
  12.     // 所以,向map中添加鍵值對時,鍵值對的值固定是PRESENT  
  13.     private static final Object PRESENT = new Object();  
  14.  
  15.     // 默認構造函數  
  16.     public HashSet() {  
  17.         // 調用HashMap的默認構造函數,創建map  
  18.         map = new HashMap<E,Object>();  
  19.     }  
  20.  
  21.     // 帶集合的構造函數  
  22.     public HashSet(Collection<? extends E> c) {  
  23.         // 創建map。  
  24.         // 爲什麼要調用Math.max((int) (c.size()/.75f) + 1, 16),從 (c.size()/.75f) + 1 和 16 中選擇一個比較大的樹呢?          
  25.         // 首先,說明(c.size()/.75f) + 1  
  26.         //   因爲從HashMap的效率(時間成本和空間成本)考慮,HashMap的加載因子是0.75。  
  27.         //   當HashMap的“閾值”(閾值=HashMap總的大小*加載因子) < “HashMap實際大小”時,  
  28.         //   就需要將HashMap的容量翻倍。  
  29.         //   所以,(c.size()/.75f) + 1 計算出來的正好是總的空間大小。  
  30.         // 接下來,說明爲什麼是 16 。  
  31.         //   HashMap的總的大小,必須是2的指數倍。若創建HashMap時,指定的大小不是2的指數倍;  
  32.         //   HashMap的構造函數中也會重新計算,找出比“指定大小”大的最小的2的指數倍的數。  
  33.         //   所以,這裏指定爲16是從性能考慮。避免重複計算。  
  34.         map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 116));  
  35.         // 將集合(c)中的全部元素添加到HashSet中  
  36.         addAll(c);  
  37.     }  
  38.  
  39.     // 指定HashSet初始容量和加載因子的構造函數  
  40.     public HashSet(int initialCapacity, float loadFactor) {  
  41.         map = new HashMap<E,Object>(initialCapacity, loadFactor);  
  42.     }  
  43.  
  44.     // 指定HashSet初始容量的構造函數  
  45.     public HashSet(int initialCapacity) {  
  46.         map = new HashMap<E,Object>(initialCapacity);  
  47.     }  
  48.  
  49.     HashSet(int initialCapacity, float loadFactor, boolean dummy) {  
  50.         map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);  
  51.     }  
  52.  
  53.     // 返回HashSet的迭代器  
  54.     public Iterator<E> iterator() {  
  55.         // 實際上返回的是HashMap的“key集合的迭代器”  
  56.         return map.keySet().iterator();  
  57.     }  
  58.  
  59.     public int size() {  
  60.         return map.size();  
  61.     }  
  62.  
  63.     public boolean isEmpty() {  
  64.         return map.isEmpty();  
  65.     }  
  66.  
  67.     public boolean contains(Object o) {  
  68.         return map.containsKey(o);  
  69.     }  
  70.  
  71.     // 將元素(e)添加到HashSet中  
  72.     public boolean add(E e) {  
  73.         return map.put(e, PRESENT)==null;  
  74.     }  
  75.  
  76.     // 刪除HashSet中的元素(o)  
  77.     public boolean remove(Object o) {  
  78.         return map.remove(o)==PRESENT;  
  79.     }  
  80.  
  81.     public void clear() {  
  82.         map.clear();  
  83.     }  
  84.  
  85.     // 克隆一個HashSet,並返回Object對象  
  86.     public Object clone() {  
  87.         try {  
  88.             HashSet<E> newSet = (HashSet<E>) super.clone();  
  89.             newSet.map = (HashMap<E, Object>) map.clone();  
  90.             return newSet;  
  91.         } catch (CloneNotSupportedException e) {  
  92.             throw new InternalError();  
  93.         }  
  94.     }  
  95.  
  96.     // java.io.Serializable的寫入函數  
  97.     // 將HashSet的“總的容量,加載因子,實際容量,所有的元素”都寫入到輸出流中  
  98.     private void writeObject(java.io.ObjectOutputStream s)  
  99.         throws java.io.IOException {  
  100.         // Write out any hidden serialization magic  
  101.         s.defaultWriteObject();  
  102.  
  103.         // Write out HashMap capacity and load factor  
  104.         s.writeInt(map.capacity());  
  105.         s.writeFloat(map.loadFactor());  
  106.  
  107.         // Write out size  
  108.         s.writeInt(map.size());  
  109.  
  110.         // Write out all elements in the proper order.  
  111.         for (Iterator i=map.keySet().iterator(); i.hasNext(); )  
  112.             s.writeObject(i.next());  
  113.     }  
  114.  
  115.  
  116.     // java.io.Serializable的讀取函數  
  117.     // 將HashSet的“總的容量,加載因子,實際容量,所有的元素”依次讀出  
  118.     private void readObject(java.io.ObjectInputStream s)  
  119.         throws java.io.IOException, ClassNotFoundException {  
  120.         // Read in any hidden serialization magic  
  121.         s.defaultReadObject();  
  122.  
  123.         // Read in HashMap capacity and load factor and create backing HashMap  
  124.         int capacity = s.readInt();  
  125.         float loadFactor = s.readFloat();  
  126.         map = (((HashSet)thisinstanceof LinkedHashSet ?  
  127.                new LinkedHashMap<E,Object>(capacity, loadFactor) :  
  128.                new HashMap<E,Object>(capacity, loadFactor));  
  129.  
  130.         // Read in size  
  131.         int size = s.readInt();  
  132.  
  133.         // Read in all elements in the proper order.  
  134.         for (int i=0; i<size; i++) {  
  135.             E e = (E) s.readObject();  
  136.             map.put(e, PRESENT);  
  137.         }  
  138.     }  

說明

HashSet的代碼實際上非常簡單,通過上面的註釋應該很能夠看懂。它是通過HashMap實現的,若對HashSet的理解有困難,建議先學習以下HashMap;學完HashMap之後,在學習HashSet就非常容易了。


通過Iterator遍歷HashSet

第一步:根據iterator()獲取HashSet的迭代器。
第二步:遍歷迭代器獲取各個元素

  1. // 假設set是HashSet對象  
  2. for(Iterator iterator = set.iterator();  
  3.        iterator.hasNext(); ) {   
  4.     iterator.next();  
  5. }     

3.2 通過for-each遍歷HashSet

第一步:根據toArray()獲取HashSet的元素集合對應的數組。
第二步:遍歷數組,獲取各個元素。

  1. // 假設set是HashSet對象,並且set中元素是String類型  
  2. String[] arr = (String[])set.toArray(new String[0]);  
  3. for (String str:arr)  
  4.     System.out.printf("for each : %s\n", str);  

HashSet的遍歷測試程序如下: 

  1. import java.util.Random;  
  2. import java.util.Iterator;  
  3. import java.util.HashSet;  
  4.  
  5. /*  
  6.  * @desc 介紹HashSet遍歷方法  
  7.  *  
  8.  * @author skywang  
  9.  */ 
  10. public class HashSetIteratorTest {  
  11.  
  12.     public static void main(String[] args) {  
  13.         // 新建HashSet  
  14.         HashSet set = new HashSet();  
  15.  
  16.         // 添加元素 到HashSet中  
  17.         for (int i=0; i<5; i++)  
  18.             set.add(""+i);  
  19.  
  20.         // 通過Iterator遍歷HashSet  
  21.         iteratorHashSet(set) ;  
  22.  
  23.         // 通過for-each遍歷HashSet  
  24.         foreachHashSet(set);  
  25.     }  
  26.  
  27.     /*  
  28.      * 通過Iterator遍歷HashSet。推薦方式  
  29.      */ 
  30.     private static void iteratorHashSet(HashSet set) {  
  31.         for(Iterator iterator = set.iterator();  
  32.                iterator.hasNext(); ) {  
  33.             System.out.printf("iterator : %s\n", iterator.next());  
  34.         }  
  35.     }  
  36.  
  37.     /*  
  38.      * 通過for-each遍歷HashSet。不推薦!此方法需要先將Set轉換爲數組  
  39.      */ 
  40.     private static void foreachHashSet(HashSet set) {  
  41.         String[] arr = (String[])set.toArray(new String[0]);  
  42.         for (String str:arr)  
  43.             System.out.printf("for each : %s\n", str);  
  44.     }  


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