JDK源碼閱讀--HashMap

 

public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {


基於hash表的Map集合,容許key=nullvalue=nullHashMap是不一樣步的,不能保證Map集合的順序,它是無序的Map集合.
HashMap有兩個參數影響它的性能: 初始化容量(initial capacity) 和 負載因子(load factor)。
初始化容量:就是建立 hash表時的容量
負載因子:負載因子是衡量在哈希表的容量被自動增長以前,哈希表被容許得到多少滿的度量。
當哈希表中的條目數超過負載因子和當前容量的乘積時,哈希表將被從新哈希(即從新構建內部數據結構)
這樣哈希表的桶數大約是桶數的兩倍。

默認的負載因子是0.75f,這是一個在時間和空間上的一個折中;較高的值減小了空間開銷,但增長了查找成本(主要表如今HaspMapgetput操做)
   若是初始容量大於最大條目數除以負載因子,則不會發生任何重哈希操做。
底層數據結構是鏈表數組,
 1 /**
 2      * 默認初始化容量是16
 3      */
 4     static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
 5 
 6     /**
 7      * 最大的容量,若是較高的值由帶參數的任何構造函數隱式指定,則使用。
 8      * 必須是2的冪,最大容量爲1073741824
 9      */
10     static final int MAXIMUM_CAPACITY = 1 << 30;//1073741824
11 
12     /**
13      * 當構造函數中沒有指定時使用的負載因子,默認是0.75f;
14      */
15     static final float DEFAULT_LOAD_FACTOR = 0.75f;
16 
17     // 一個桶的樹化閾值
18     // 當桶中元素個數超過這個值時,須要使用紅黑樹節點替換鏈表節點
19     // 這個值必須爲 8,要否則頻繁轉換效率也不高
20     static final int TREEIFY_THRESHOLD = 8;
21 
22     // 一個樹的鏈表還原閾值
23     // 當擴容時,桶中元素個數小於這個值,就會把樹形的桶元素 還原(切分)爲鏈表結構
24     // 這個值應該比上面那個小,至少爲 6,避免頻繁轉換
25     static final int UNTREEIFY_THRESHOLD = 6;
26 
27 
28     // 哈希表的最小樹形化容量
29     // 當哈希表中的容量大於這個值時,表中的桶才能進行樹形化
30     // 不然桶內元素太多時會擴容,而不是樹形化
31     // 爲了不進行擴容、樹形化選擇的衝突,這個值不能小於 4 * TREEIFY_THRESHOLD
32     static final int MIN_TREEIFY_CAPACITY = 64;
 

 

基本的hash表節點:node

 1     /**
 2      * 基本的hash表節點,
 3      * (參見下面的forTreeNode子類,以及LinkedHashMap中的條目子類。)
 4      */
 5     static class Node<K,V> implements Map.Entry<K,V> {
 6         final int hash;//hash值 final修飾的
 7         final K key;//鍵  final修飾的
 8         V value;//
 9         Node<K,V> next;//後置節點
10 
11         //構造函數
12         Node(int hash, K key, V value, Node<K,V> next) {
13             this.hash = hash;
14             this.key = key;
15             this.value = value;
16             this.next = next;
17         }
18 
19         public final K getKey()        { return key; }
20         public final V getValue()      { return value; }
21         public final String toString() { return key + "=" + value; }
22 
23         public final int hashCode() {
24             return Objects.hashCode(key) ^ Objects.hashCode(value);
25         }
26 
27         public final V setValue(V newValue) {
28             V oldValue = value;
29             value = newValue;
30             return oldValue;
31         }
32 
33         public final boolean equals(Object o) {
34             if (o == this)
35                 return true;
36             if (o instanceof Map.Entry) {
37                 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
38                 if (Objects.equals(key, e.getKey()) &&
39                     Objects.equals(value, e.getValue()))
40                     return true;
41             }
42             return false;
43         }
44     }

 

 

 1 /**
 2      * The table, initialized on first use, and resized as
 3      * necessary. When allocated, length is always a power of two.
 4      * (We also tolerate length zero in some operations to allow
 5      * bootstrapping mechanics that are currently not needed.)
 6      * 第一次初始化的時候使用這個表。當分配時,長度老是2的冪
 7      */
 8     transient Node<K,V>[] table;
 9 
10     /**
11      * Holds cached entrySet(). Note that AbstractMap fields are used for keySet() and values().
12      * 保存緩存entrySet()。注意AbstractMap字段用於keySet()和values()。
13      */
14     transient Set<Map.Entry<K,V>> entrySet;
15 
16     /**
17      * The number of key-value mappings contained in this map.
18      * 此映射中包含的鍵值映射的數目。
19      */
20     transient int size;
21 
22     /**
23      * 被修改的次數
24      */
25     transient int modCount;
26 
27 
28 
29     /**
30      * The next size value at which to resize (capacity * load factor).
31      * 要調整大小的下一個大小值(容量*負載因子)。
32      * @serial
33      */
34     int threshold;//要調整大小的下一個大小值(容量*負載因子)。
35 
36     /**
37      * 哈希表的負載因子。
38      * @serial
39      */
40     final float loadFactor;

 

 

構造函數:bootstrap

 1 /**
 2      * 構造一個空的HashMap,使用專門的初始化容量和默認的負載因子。
 3      * @param  initialCapacity the initial capacity.
 4      * @throws IllegalArgumentException if the initial capacity is negative.
 5      */
 6     public HashMap(int initialCapacity) {
 7         this(initialCapacity, DEFAULT_LOAD_FACTOR);
 8     }
 9 
10     /**
11      * (16) and the default load factor (0.75).
12      * 構造一個空的HashMap,使用默認的初始化容量(16)和默認的負載因子(0.75f)。
13      */
14     public HashMap() {
15         this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
16     }
17 
18     /**
19      * 構造一個新的HashMap,使用與指定映射相同的映射。裝載因子使用默認的(0.75f),和足夠容納指定Map中的映射的初始容量。
20      * @param   m the map whose mappings are to be placed in this map 要在此映射中放置其映射的映射的映射
21      * @throws  NullPointerException if the specified map is null 若是這個指定的map爲null,則拋出空指針異常NullPointerException
22      */
23     public HashMap(Map<? extends K, ? extends V> m) {
24         this.loadFactor = DEFAULT_LOAD_FACTOR;
25         putMapEntries(m, false);
26     }

 

 1  /**
 2      * 實現 Map.putAll和 Map的構造函數
 3      * @param m the map
 4      * @param evict  當最初構造這個map的時候爲false,不然爲true(傳遞到以後的插入節點的方法中)。
 5      */
 6     final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
 7         int s = m.size();
 8         if (s > 0) {
 9             if (table == null) { // pre-size
10                 float ft = ((float)s / loadFactor) + 1.0F;
11                 int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY);
12                 if (t > threshold) {
13                     threshold = tableSizeFor(t);
14                 }
15             }else if (s > threshold) {
16                 resize();
17             }
18             for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
19                 K key = e.getKey();
20                 V value = e.getValue();
21                 putVal(hash(key), key, value, false, evict);
22             }
23         }
24     }

 

 

 1 /**
 2      * @return 返回此映射中鍵值映射的數目。
 3      */
 4     public int size() {
 5         return size;
 6     }
 7 
 8     /**
 9      *  判斷該map是否爲空
10      * @return  當此map中不含鍵值對的時候,返回true.
11      */
12     public boolean isEmpty() {
13         return size == 0;
14     }

 

get方法(先比較hash,若相等在比較equals):數組

1. Key==null的時候,判斷map也爲空,就返回null
2. 根據node數組下標找到node數組下標
3. 若是當前node鏈表只存在一個數據就直接取value值
若是當前node鏈表存在多個node元素,則循環遍歷node鏈表,分別對他們的hash值和key值,value值進行判斷,知道找到node之後返回Value值,若是沒有找到返回null。緩存

 1  /**
 2      * 根據鍵key獲取值value,若是此map不包含這個key,則返回null。
 3      * Returns the value to which the specified key is mapped,
 4      * or {@code null} if this map contains no mapping for the key.
 5      *
 6      * <p>More formally, if this map contains a mapping from a key
 7      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
 8      * key.equals(k))}, then this method returns {@code v}; otherwise
 9      * it returns {@code null}.  (There can be at most one such mapping.)
10      *
11      * <p>A return value of {@code null} does not <i>necessarily</i>
12      * indicate that the map contains no mapping for the key; it's also
13      * possible that the map explicitly maps the key to {@code null}.
14      * The {@link #containsKey containsKey} operation may be used to
15      * distinguish these two cases.
16      *
17      * @see #put(Object, Object)
18      */
19     public V get(Object key) {
20         Node<K,V> e;
21         return (e = getNode(hash(key), key)) == null ? null : e.value;
22     }
23 
24     /**
25      * Implements Map.get and related methods
26      * Map.get相關方法的實現
27      * @param hash hash for key  鍵key的hash值
28      * @param key the key        鍵key
29      * @return the node, or null if none  返回根據key及key的hash找到的這個節點;若是這個節點爲空,則返回null
30      *
31      *
32      */
33     final Node<K,V> getNode(int hash, Object key) {
34         Node<K,V>[] tab;
35         Node<K,V> first, e;
36         int n;
37         K k;
38         if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
39             // always check first node 老是會檢查哈希表的第一個節點
40             if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) {
41                 return first;
42             }
43             if ((e = first.next) != null) {//若是第一個節點的下一個節點不爲空
44                 if (first instanceof TreeNode) {//若是該節點是TreeNode類型的(紅黑樹)
45                     return ((TreeNode<K, V>) first).getTreeNode(hash, key);//調用紅黑樹的查找方法
46                 }
47                 do {
48                     if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
49                         return e;
50                     }
51                 } while ((e = e.next) != null);
52             }
53         }
54         return null;
55     }

 

put方法:數據結構

1. 首先判斷node數組,也就是數組是否爲空,爲空的話就初始化hashmap
2. 若是node數組不爲空的話,就判斷key是否爲空,爲空的話就將放到數組的第一個位置
3. 就經過key獲取hash值,經過hash值作比較,若是key的hash值相等 而且key.equals(e.key)也相等的話,就將新的value替換掉舊的。若是條件不知足就建立一個node,且用上一個node的next指向新建立的node 。app

 1 /**
 2      * 往map中添加新的鍵值對,若是鍵key存在,則將該鍵key對應的舊值value替換爲新值value
 3      *
 4      * @param key 要與指定值關聯的鍵
 5      * @param value 值與指定的鍵關聯
 6      * @return 與key關聯的前一個值,若是沒有key的映射,則爲null。(null返回值還能夠指示之前將null與key關聯的映射。)
 7      */
 8     public V put(K key, V value) {
 9         return putVal(hash(key), key, value, false, true);
10     }
11 
12     /**
13      * Implements Map.put and related methods
14      * 實現Map.put和相關方法
15      * @param hash hash for key    key的hash值
16      * @param key the key           鍵
17      * @param value the value to put  值
18      * @param onlyIfAbsent   若是是true,不能改變已存在的值
19      * @param evict  若是爲false,該表處於建立模式
20      * @return  返回前一個值,若是沒有,則爲空
21      * 根據key算hash,根據容量和hash算index,table[index]沒有直接添加到數組中,table[index]有,若index位置同一個key則更新,
22      *      不然遍歷next是否有,有則更新,無則新增,最後根據thread與size判斷是否擴容。注:擴容時容量翻倍,從新算hash複製到新數組
23      *
24      */
25     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
26                    boolean evict) {
27         Node<K,V>[] tab;
28         Node<K,V> p;
29         int n, i;
30         if ((tab = table) == null || (n = tab.length) == 0) {
31             n = (tab = resize()).length;
32         }
33         if ((p = tab[i = (n - 1) & hash]) == null) {
34             tab[i] = newNode(hash, key, value, null);
35         }else {
36             Node<K,V> e; K k;
37             if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) {
38                 e = p;
39             }else if (p instanceof TreeNode) {
40                 e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
41             }
42             else {
43                 for (int binCount = 0; ; ++binCount) {
44                     if ((e = p.next) == null) {
45                         p.next = newNode(hash, key, value, null);
46                         if (binCount >= TREEIFY_THRESHOLD - 1) { // -1 for 1st
47                             treeifyBin(tab, hash);
48                         }
49                         break;
50                     }
51                     if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
52                         break;
53                     }
54                     p = e;
55                 }
56             }
57             if (e != null) { // existing mapping for key
58                 V oldValue = e.value;
59                 if (!onlyIfAbsent || oldValue == null) {
60                     e.value = value;
61                 }
62                 afterNodeAccess(e);
63                 return oldValue;
64             }
65         }
66         ++modCount;
67         if (++size > threshold) {
68             resize();
69         }
70         afterNodeInsertion(evict);
71         return null;
72     }
相關文章
相關標籤/搜索