1. 程式人生 > >淺析Hashmap原始碼

淺析Hashmap原始碼

  屌絲程式設計師的奮鬥之路現在開始

               java集合這一塊無論在面試或在寫程式碼中,我們都會接觸到,所以java集合是特別重要的,其中HashMap更是被我們經常用到。

 一.概括

               HashMap是用鍵值對的既已key-value的形式來儲存值的,當然這只是展現給大家的一種表象,key和value都可以為空,但是key不能重複,HashMap不是現線安全的,如果想讓HashMap變成現線安全的,可以呼叫Collections的靜態方法synchronized方法。其實HashMap是用一個動態陣列和多個連結串列來存放key-value的

,key-value不是直接放在陣列和連結串列裡面的,key-value是被一個叫Entry的物件給封裝了,所以動態資料和連結串列裡面是存放的Entry物件的。

二.HashMap的資料結構

               HashMap可以說是由一個動態陣列和多個連結串列組成,連結串列是接在每一個數組單元下面的,動態陣列和連結串列中儲存的單元是一個叫Entry的物件,從下面的圖中可以很直觀的看出HashMap的資料結構,其中每一個單元格儲存的就是Entry物件了,這一個Entry物件是HashMap的一個靜態類

             

            Entry原始碼          

[java]  view plain  copy
  1. static class Entry<K,V> implements Map.Entry<K,V> {  
  2.        final K key;  
  3.        V value;  
  4.        Entry<K,V> next;//指向一下個Entry物件,他是為解決hash衝突而存在的。  
  5.        int hash;  
  6.   
  7.        /** 
  8.         * Creates new entry. 
  9.         */  
  10.        Entry(int h, K k, V v, Entry<K,V> n) {  
  11.            value = v;  
  12.            next = n;  
  13.            key = k;  
  14.            hash = h;  
  15.        }  
  16.   
  17.        public final K getKey() {  
  18.            return key;  
  19.        }  
  20.   
  21.        public final V getValue() {  
  22.            return value;  
  23.        }  
  24.   
  25.        public final V setValue(V newValue) {  
  26.            V oldValue = value;  
  27.            value = newValue;  
  28.            return oldValue;  
  29.        }  
  30.   
  31.        public final boolean equals(Object o) {  
  32.            if (!(o instanceof Map.Entry))  
  33.                return false;  
  34.            Map.Entry e = (Map.Entry)o;  
  35.            Object k1 = getKey();  
  36.            Object k2 = e.getKey();  
  37.            if (k1 == k2 || (k1 != null && k1.equals(k2))) {  
  38.                Object v1 = getValue();  
  39.                Object v2 = e.getValue();  
  40.                if (v1 == v2 || (v1 != null && v1.equals(v2)))  
  41.                    return true;  
  42.            }  
  43.            return false;  
  44.        }  
  45.   
  46.        public final int hashCode() {  
  47.            return (key==null   ? 0 : key.hashCode()) ^  
  48.                   (value==null ? 0 : value.hashCode());  
  49.        }  
  50.   
  51.        public final String toString() {  
  52.            return getKey() + "=" + getValue();  
  53.        }  
  54.   
  55.        /** 
  56.         * This method is invoked whenever the value in an entry is 
  57.         * overwritten by an invocation of put(k,v) for a key k that's already 
  58.         * in the HashMap. 
  59.         */  
  60.        void recordAccess(HashMap<K,V> m) {  
  61.        }  
  62.   
  63.        /** 
  64.         * This method is invoked whenever the entry is 
  65.         * removed from the table. 
  66.         */  
  67.        void recordRemoval(HashMap<K,V> m) {  
  68.        }  
  69.    }  

        從Entry的屬性中看到了我們所熟悉的key和value,沒錯,這就是我們在用HashMap的時候所要接觸到的key,value,Entry對key-value進行了封裝,我們再看看Enrty的next屬性,儲存的就是指向下一個物件的指標,當然java是沒有指標這一說的,我覺得在這裡將它當成指標更好理解,next在出現hash衝突的時候會發生作用,現在我們再看看上面的那一張圖,現在知道為什麼那些綠色的連結串列是怎麼連線起來的了吧,就是通過Entry的next屬性指向下一個Entry物件連線起來的,所以在HashMap原始碼中是看不到動態連結串列的定義,但是它確實是存在的。

三.HashMap的API

     1.HashMap的相關屬性

[java]  view plain  copy
  1. /** 
  2.  * HashMap中陣列的預設大小是16 
  3.  */  
  4. static final int DEFAULT_INITIAL_CAPACITY = 16;  
  5.   
  6. /** 
  7.  * 陣列的最大長度 
  8.  */  
  9. static final int MAXIMUM_CAPACITY = 1 << 30;  
  10.   
  11. /** 
  12.  * 預設的載入因子是0.75 
  13.  */  
  14. static final float DEFAULT_LOAD_FACTOR = 0.75f;  
  15.   
  16. /** 
  17.  * 存放Entry物件的陣列,也是HashMap存放資料的地方 
  18.  */  
  19. transient Entry<K,V>[] table;  
  20.   
  21. /** 
  22.  * HashMap的存入值得個數,注意:他和陣列的大小是沒有關係的 
  23.  */  
  24. transient int size;  
  25.   
  26. /** 
  27.  * 邊界值  <span style="font-family: Arial, Helvetica, sans-serif;">邊界值=HahsMap的容量*載入因子</span> 
  28.  * @serial 
  29.  */  
  30. int threshold;  
  31.   
  32. /** 
  33.  *載入因子 
  34.  * @serial 
  35.  */  
  36. final float loadFactor;  
            
         邊界值=陣列大小*載入因子

當HashMap所儲存物件的個數超過邊界值的時候就會對陣列進行擴容,例如HashMap預設的載入因子是0.75,陣列預設的大小是16,所以邊界值是12,當我們在HashMap中儲存的值大於等於12的時候,HashMap會對陣列table進行2倍的擴容。
       

    2.HashMap的構造方法

[java]  view plain  copy
  1. /** 
  2.  *給陣列設定初始容量和載入因子 
  3.  */  
  4. public HashMap(int initialCapacity, float loadFactor) {  
  5.         if (initialCapacity < 0)  
  6.             throw new IllegalArgumentException("Illegal initial capacity: " +  
  7.                                                initialCapacity);  
  8.         if (initialCapacity > MAXIMUM_CAPACITY)  
  9.             initialCapacity = MAXIMUM_CAPACITY;  
  10.         if (loadFactor <= 0 || Float.isNaN(loadFactor))  
  11.             throw new IllegalArgumentException("Illegal load factor: " +  
  12.                                                loadFactor);  
  13.