隨筆3 HashMap


 

 

equals、hashcode和==的區別

在介紹HashMap之前,我想先闡述一下我對這三者的理解,equals這個方法呢,就是在判斷是否為同一對象(注意,這里的同一對象和相同的內存地址是不同的),是否為同一對象其實看一看做一種我們對事物的主觀定義,如果我是個佛系青年,認為世間萬物都是相同的,那么我只需要在equals里只return一個true。hashcode我們可以看做是一個對象的表示符,同一對象的表示符肯定是一樣的,不同對象的表示符理論上來說應該是不同的,但是現實永遠不太盡如人意,不同的對象hashcode相同,就是所謂的沖突。所以當我們重寫Object中的equals方法的時候,一定要記得重寫hashcode方法==很好理解,它就表示是不是同一內存地址。

 

接下來我們就從原理和源碼兩方面去介紹一下hashcode,並且對hashcode的非線程安全進行一些簡單的討論,下文參考了https://www.cnblogs.com/softidea/p/7261111.html這篇文章

 

一、HashMap原理

在最初的HashMap中,其底層的實現數組加鏈表的數據結構,基本單元為Entry,但是在java8之后進行了優化,增加了紅黑樹,底層結構也由Entry變成了Node和TreeNode組合完成,但是TreeNode其實還是繼承自Node。

數組的優缺點:優點是根據下標進行查找,十分迅速,缺點是在數組中插入元素,刪除元素效率極低

鏈表的優缺點:優點是對於增刪操作非常方便 ,但是查找起來卻很慢

紅黑樹優缺點:優點是查找非常迅速,缺點是插入元素的時候又費時間又費空間

HashMap就是綜合了以上幾點,構成的一種數據結構:首先用一個數組來構成散列表,然后用鏈表來解決沖突,當沖突項大於默認值8時,會將鏈表轉化成一顆紅黑樹,提高查詢效率(鏈表就是一顆退化樹)。

如下圖所示:

從上圖我們可以發現HashMap是由Entry數組+鏈表組成的,一個長度為16的數組中,每個元素存儲的是一個鏈表的頭結點。那么這些元素是按照什么樣的規則存儲到數組中呢。一般情況是通過hash(key)%len獲得,也就是元素的key的哈希值對數組長度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存儲在數組下標為12的位置。

二、源碼分析

1、底層數據類

 1     /**
 2      * 這里可見,Node是實現了Entry
 3      */
 4     static class Node<K,V> implements Map.Entry<K,V> {
 5         /**
 6          * 注意hash和key都是final修飾的,說明作為key需要是不可變值,比如String很常用
 7          * 如果采用自己創建的對象
 8          */
 9         final int hash;
10         final K key;
11         //value是可變的
12         V value;
13         //指向下一個節點的指針
14         Node<K,V> next;
15 
16         Node(int hash, K key, V value, Node<K,V> next) {
17             this.hash = hash;
18             this.key = key;
19             this.value = value;
20             this.next = next;
21         }
22 
23         public final K getKey()        { return key; }
24         public final V getValue()      { return value; }
25         public final String toString() { return key + "=" + value; }
26         
27         /**
28          * key的hash值和value的hash值做與操作,所以key和value需要重寫hashCode方法
29          */
30         public final int hashCode() {
31             return Objects.hashCode(key) ^ Objects.hashCode(value);
32         }
33 
34         //這里設置新值的時候會返回舊值
35         public final V setValue(V newValue) {
36             V oldValue = value;
37             value = newValue;
38             return oldValue;
39         }
40 
41         //這里需要注意要重寫equals方法
42         public final boolean equals(Object o) {
43             if (o == this)
44                 return true;
45             if (o instanceof Map.Entry) {
46                 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
47                 if (Objects.equals(key, e.getKey()) &&
48                     Objects.equals(value, e.getValue()))
49                     return true;
50             }
51             return false;
52         }
53     }
 1     /**
 2      * 這里介紹一下紅黑樹的五條性質
 3      * 1、節點是紅色或者是黑色;
 4      * 2、根節點是黑色;
 5      * 3、每個葉節點(NIL或空節點)是黑色;
 6      * 4、每個紅色節點的兩個子節點都是黑色的(也就是說不存在兩個連續的紅色節點);
 7      * 5、從任一節點到其沒個葉節點的所有路徑都包含相同數目的黑色節點;
 8      */
 9     static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
10         TreeNode<K,V> parent;  // red-black tree links
11         TreeNode<K,V> left;
12         TreeNode<K,V> right;
13         TreeNode<K,V> prev;    // needed to unlink next upon deletion
14         boolean red;
15     }

2、HashMap的常量和屬性

 1     public class HashMap<K,V> extends AbstractMap<K,V>
 2     implements Map<K,V>, Cloneable, Serializable {
 3         
 4         /**
 5          * 默認初始化容量,必須是2的次方。這個容量就是table的長度
 6          */
 7         static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
 8 
 9         /**
10          * 這里指定了一個容量的上限,如果自己指定的值大於上限的話,就采用該默認值
11          */
12         static final int MAXIMUM_CAPACITY = 1 << 30;
13 
14         /**
15          * 默認加載因子,當沒有指定加載因子的時候會采用該值,這個值的意義在於,當有效值比容量大於加載因子時
16          * 會擴容table數組
17          */
18         static final float DEFAULT_LOAD_FACTOR = 0.75f;
19 
20         /**
21          * 這個是一個鏈表在多長時轉化為紅黑樹,默認為8
22          */
23         static final int TREEIFY_THRESHOLD = 8;
24 
25         /**
26          * 當一顆樹的節點少於6個的時候,將這棵樹轉化為鏈表
27          */
28         static final int UNTREEIFY_THRESHOLD = 6;
29 
30         /**
31          * 當哈希表中的容量大於這個值時,表中的桶才能進行樹形化 否則桶內元素太多時會擴容,而不是樹形化 為了
32          * 避免進行擴容、樹形化選擇的沖突,這個值不能小於 4 * TREEIFY_THRESHOLD
33          */
34         static final int MIN_TREEIFY_CAPACITY = 64;
35         /**
36          * 這個數組會在第一次使用時初始化,並且在條件合適的時候重構,它的長度一定是2的整數次方
37          */
38         transient Node<K,V>[] table;
39     
40         /**
41          * 一個包含所有節點的Set
42          */
43         transient Set<Map.Entry<K,V>> entrySet;
44     
45         /**
46          * Map中的當前元素數量
47          */
48         transient int size;
49     
50         /**
51          * 這個是Map當中元素的修改次數(這里的修改只是說增加和減少元素時,該量會加一)
52          */
53         transient int modCount;
54     
55         /**
56          * 當大於這個值的時候會執行重構數組操作(capacity * load factor).
57          */
58         int threshold;
59     
60         /**
61          * 自定義的加載因子
62          */
63         final float loadFactor;
64     }

3、HashMap的resize

 1     final Node<K,V>[] resize() {
 2         //將原來的table指針保存
 3         Node<K,V>[] oldTab = table;    
 4         //獲取原來數組的長度,oldTab為null說明還沒有進行初始化
 5         int oldCap = (oldTab == null) ? 0 : oldTab.length;
 6         //保存以前重構table的閾值
 7         int oldThr = threshold;
 8         int newCap, newThr = 0;
 9         //oldCap > 0表示已經初始化過了
10         if (oldCap > 0) {
11             //當原來的容量已經達到最大容量的時候,將閾值設置為Integer.MAX_VALUE,這樣就不會再發生重構的情況
12             if (oldCap >= MAXIMUM_CAPACITY) {
13                 threshold = Integer.MAX_VALUE;
14                 return oldTab;
15             }
16             //否則將舊的容量擴大兩倍,當它小於最大容量,並且舊的容量大於初始化最小容量的時候,將新的閾值設置為舊的閾值的兩倍
17             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
18                      oldCap >= DEFAULT_INITIAL_CAPACITY)
19                 newThr = oldThr << 1; // double threshold
20         }
21         else if (oldThr > 0) //雖然還沒有初始化,但是設置過了閾值,將舊的閾值設置為新的容量
22             newCap = oldThr;
23         else {               //沒有初始化閾值的時候采用默認算法計算閾值
24             newCap = DEFAULT_INITIAL_CAPACITY;
25             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
26         }
27         if (newThr == 0) {//對應oldCap = 0 && oldThr > 0的情況
28             float ft = (float)newCap * loadFactor;
29             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
30                       (int)ft : Integer.MAX_VALUE);
31         }
32         threshold = newThr;
33         @SuppressWarnings({"rawtypes","unchecked"})
34             Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
35         table = newTab;
36         if (oldTab != null) {
37             //將舊數組中的元素全部取出,重新映射到新數組中
38             for (int j = 0; j < oldCap; ++j) {
39                 Node<K,V> e;
40                 if ((e = oldTab[j]) != null) {
41                     oldTab[j] = null;
42                     if (e.next == null)
43                         //(newCap - 1)是一個尾部全部為1的數
44                         newTab[e.hash & (newCap - 1)] = e;
45                     else if (e instanceof TreeNode)//判斷舊的節點是一個樹節點,則對樹進行操作,重構樹或者變成鏈表等等
46                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
47                     else { 
48                         // 對原來的鏈表部分進行重構
49                         Node<K,V> loHead = null, loTail = null;
50                         Node<K,V> 所以新索引要么是原索引,要不就是原索引+oldCap = null, hiTail = null;
51                         Node<K,V> next;
52                         do {
53                             next = e.next;
54                             if ((e.hash & oldCap) == 0) {
55                                 if (loTail == null)
56                                     loHead = e;
57                                 else
58                                     loTail.next = e;
59                                 loTail = e;
60                             }
61                             else {
62                                 if (hiTail == null)
63                                     hiHead = e;
64                                 else
65                                     hiTail.next = e;
66                                 hiTail = e;
67                             }
68                         } while ((e = next) != null);
69                         //在重新計算hash之后,因為n變為2倍,那么n-1的mask范圍在高位多1bit
70                         //對原來的鏈表部分進行重構
71                         if (loTail != null) {
72                             loTail.next = null;
73                             newTab[j] = loHead;
74                         }
75                         if (hiTail != null) {
76                             hiTail.next = null;
77                             newTab[j + oldCap] = hiHead;
78                         }
79                     }
80                 }
81             }
82         }
83         return newTab;
84     }

舉個例子(例子來自https://blog.csdn.net/lianhuazy167/article/details/66967698

 

 

4、修改方法

4.1、put

 1     public V put(K key, V value) {
 2         return putVal(hash(key), key, value, false, true);
 3     }
 4 
 5     /**
 6      * @param hash 計算出來key的hash值
 7      * @param key key的值
 8      * @param value value的值
 9      * @param onlyIfAbsent 當為true的時候,如果key對應有值,則不修改這個值
10      * @param evict 當為false時,表示這個處於創建模式,現在由於afterNodeInsertion中什么都沒有,這里沒有實際意
11      *           evict參數用於LinkedHashMap中的尾部操作
12      */
13     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
14                    boolean evict) {
15         Node<K,V>[] tab; Node<K,V> p; int n, i;
16         //判斷當table為null或者tab的長度為0時,即table尚未初始化,此時通過resize()方法得到初始化的table
17         if ((tab = table) == null || (n = tab.length) == 0)
18             n = (tab = resize()).length;
19         //當p為null時,表明tab[i]上沒有任何元素,那么接下來就new第一個Node節點,調用newNode方法返回新節點賦值給tab[i]
20         if ((p = tab[i = (n - 1) & hash]) == null)
21             tab[i] = newNode(hash, key, value, null);
22         else {
23             Node<K,V> e; K k;
24             //HashMap中判斷key相同的條件是key的hash相同,並且符合equals方法。這里判斷了p.key是否和插入的key相等,如果相等,則將p的引用賦給e
25             //這里為什么要把p賦值給e,而不是直接覆蓋原值呢?答案很簡單,現在我們只判斷了第一個節點,后面還可能出現key相同,所以需要在最后一並處理
26             if (p.hash == hash &&
27                 ((k = p.key) == key || (key != null && key.equals(k))))
28                 e = p;
29             else if (p instanceof TreeNode)
30                 //現在開始了第一種情況,p是紅黑樹節點,那么肯定插入后仍然是紅黑樹節點,所以我們直接強制轉型p后調用TreeNode.putTreeVal方法,返回的引用賦給e
31                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
32             else {
33                 for (int binCount = 0; ; ++binCount) {
34                     //最后一個參數為新節點的next,這里傳入null,保證了新節點繼續為該鏈表的末端
35                     if ((e = p.next) == null) {
36                         p.next = newNode(hash, key, value, null);
37                       //插入成功后,要判斷是否需要轉換為紅黑樹,因為插入后鏈表長度加1,而binCount並不包含新節點,所以判斷時要將臨界閾值減1
38                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
39                             treeifyBin(tab, hash);
40                         break;
41                     }
42                     //在遍歷鏈表的過程中,我之前提到了,有可能遍歷到與插入的key相同的節點,此時只要將這個節點引用賦值給e,最后通過e去把新的value覆蓋掉就可以了
43                     if (e.hash == hash &&
44                         ((k = e.key) == key || (key != null && key.equals(k))))
45                         break;
46                     p = e;
47                 }
48             }
49             //左邊注釋為jdk自帶注釋,說的很明白了,針對已經存在key的情況做處理
50             if (e != null) { // existing mapping for key
51                 V oldValue = e.value;
52                 if (!onlyIfAbsent || oldValue == null)
53                     e.value = value;
54                 afterNodeAccess(e);
55                 return oldValue;
56             }
57         }
58         //收尾工作,值得一提的是,對key相同而覆蓋oldValue的情況,在前面已經return,不會執行這里,所以那一類情況不算數據結構變化,並不改變modCount值
59         ++modCount;
60         //當HashMap中存在的node節點大於threshold時,hashmap進行擴容
61         if (++size > threshold)
62             resize();
63         afterNodeInsertion(evict);
64         return null;
65     }

4.2、putAll

 1     public void putAll(Map<? extends K, ? extends V> m) {
 2         putMapEntries(m, true);
 3     }
 4     
 5     /**
 6      * @param m 需要放入的Map
 7      * @param evict 在此處並無意義
 8      */
 9     final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
10         //獲取添加元素的數量
11         int s = m.size();
12         //添加的Map中有元素
13         if (s > 0) {
14             //table == null表達現在還沒有被初始化
15             if (table == null) { // pre-size
16                 //通過加載因子計算出大概需要初始化的空間
17                 float ft = ((float)s / loadFactor) + 1.0F;
18                 //檢查這個需要的空間有沒有大於最大容量MAXIMUM_CAPACITY
19                 int t = ((ft < (float)MAXIMUM_CAPACITY) ?
20                          (int)ft : MAXIMUM_CAPACITY);
21                 //如果大於了當前resize()的閾值,就要重新計算
22                 if (t > threshold)
23                     //這里會得到一個比t大的最小的2的整數次冪的值
24                     threshold = tableSizeFor(t);
25             }
26             else if (s > threshold)//在已經創建Map的情況下,s如果直接大於閾值,直接重構現在的Map
27                 resize();
28             //將傳入的Map的每個值都插入到現在的Map中
29             for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
30                 K key = e.getKey();
31                 V value = e.getValue();
32                 putVal(hash(key), key, value, false, evict);
33             }
34         }
35     }

4.3、remove

 1     public V remove(Object key) {
 2         Node<K,V> e;
 3         return (e = removeNode(hash(key), key, null, false, true)) == null ?
 4             null : e.value;
 5     }
 6 
 7     /**
 8      * @param hash key的hash值
 9      * @param key key的值
10      * @param value 傳入匹配的value值,如果matchValue=false,直接忽略
11      * @param matchValue 為true時,會去進一步匹配value
12      * @param movable if false do not move other nodes while removing
13      * @return the node, or null if none
14      */
15     final Node<K,V> removeNode(int hash, Object key, Object value,
16                                boolean matchValue, boolean movable) {
17         Node<K,V>[] tab; Node<K,V> p; int n, index;
18         //確定table已經被初始化,並且其中有元素,並且對應的 hash值有元素
19         if ((tab = table) != null && (n = tab.length) > 0 &&
20             (p = tab[index = (n - 1) & hash]) != null) {
21             Node<K,V> node = null, e; K k; V v;
22             //判斷當前這個找到的元素是不是目標元素,如果是的話賦值給node
23             if (p.hash == hash &&
24                 ((k = p.key) == key || (key != null && key.equals(k))))
25                 node = p;
26             //不是的話,就從相同hash值的所有元素中去查找
27             else if ((e = p.next) != null) {
28                 if (p instanceof TreeNode)
29                     //該列表已經轉換成紅黑樹的情況
30                     node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
31                 else {
32                     //在列表中查找的情況
33                     do {
34                         if (e.hash == hash &&
35                             ((k = e.key) == key ||
36                              (key != null && key.equals(k)))) {
37                             node = e;
38                             break;
39                         }
40                         p = e;
41                     } while ((e = e.next) != null);
42                 }
43             }
44             //找到了目標節點
45             if (node != null && (!matchValue || (v = node.value) == value ||
46                                  (value != null && value.equals(v)))) {
47                 if (node instanceof TreeNode)//是紅黑樹節點的情況
48                     ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
49                 else if (node == p)//是一個鏈表開頭元素的情況
50                     tab[index] = node.next;
51                 else
52                     p.next = node.next;//是一個鏈表中間元素的情況
53                 ++modCount;//結構改變,需要加一
54                 --size;
55                 afterNodeRemoval(node);
56                 return node;
57             }
58         }
59         return null;
60     }

4.4、clear

 1     public void clear() {
 2         Node<K,V>[] tab;
 3         modCount++;//對table結構修改加一
 4         if ((tab = table) != null && size > 0) {
 5             size = 0;
 6             //釋放數組的每一個指針
 7             for (int i = 0; i < tab.length; ++i)
 8                 tab[i] = null;
 9         }
10     }

5、查詢方法

 1     public V get(Object key) {
 2         Node<K,V> e;
 3         return (e = getNode(hash(key), key)) == null ? null : e.value;
 4     }
 5     
 6     public boolean containsKey(Object key) {
 7         return getNode(hash(key), key) != null;
 8     }
 9 
10     /**
11      * Implements Map.get and related methods
12      *
13      * @param hash hash for key
14      * @param key the key
15      * @return the node, or null if none
16      */
17     final Node<K,V> getNode(int hash, Object key) {
18         Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
19         //根據輸入的hash值,可以直接計算出對應的下標(n - 1)& hash,縮小查詢范圍,如果存在結果,則必定在table的這個位置上
20         if ((tab = table) != null && (n = tab.length) > 0 &&
21             (first = tab[(n - 1) & hash]) != null) {
22             //判斷第一個存在的節點的key是否和查詢的key相等。如果相等,直接返回該節點
23             if (first.hash == hash && // always check first node
24                 ((k = first.key) == key || (key != null && key.equals(k))))
25                 return first;
26           //遍歷該鏈表/紅黑樹直到next為null
27             if ((e = first.next) != null) {
28                  //當這個table節點上存儲的是紅黑樹結構時,在根節點first上調用getTreeNode方法,在內部遍歷紅黑樹節點,查看是否有匹配的TreeNode
29                 if (first instanceof TreeNode)
30                     return ((TreeNode<K,V>)first).getTreeNode(hash, key);
31               //當這個table節點上存儲的是鏈表結構時,方法同上
32                 do {
33                     if (e.hash == hash &&
34                         ((k = e.key) == key || (key != null && key.equals(k))))
35                         return e;
36                 } while ((e = e.next) != null);
37             }
38         }
39         return null;
40     }

 6、靜態方法

 1     /**
 2      * 對於這個函數,我想說兩點,第一點是支持key==null,返回位置為0
 3      * 第二點是(h = key.hashCode()) ^ (h >>> 16),一個int32bit
 4      * 這里正好將高16位移到了低16位,然后產生的hash值即包含了高位信息又包含了低位信息
 5      * 還解決了地址空間不夠引起的沖突問題
 6      */
 7     static final int hash(Object key) {
 8         int h;
 9         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
10     }
11 
12     /**
13      * 當x的類型為X,且X直接實現了Comparable接口(比較類型必須為X類本身)時,返回x的運行時類型;否則返回null。
14      */
15     static Class<?> comparableClassFor(Object x) {
16         if (x instanceof Comparable) {// 判斷是否實現了Comparable接口
17             Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
18             if ((c = x.getClass()) == String.class) // bypass checks
19                 return c; // 如果是String類型,直接返回String.class
20             if ((ts = c.getGenericInterfaces()) != null) {// 判斷是否有直接實現的接口
21                 for (int i = 0; i < ts.length; ++i) { // 遍歷直接實現的接口
22                     if (((t = ts[i]) instanceof ParameterizedType) &&// 該接口實現了泛型
23                         ((p = (ParameterizedType)t).getRawType() ==// 獲取接口不帶參數部分的類型對象
24                          Comparable.class) &&//  該類型是Comparable
25                         (as = p.getActualTypeArguments()) != null && // 獲取泛型參數數組
26                         as.length == 1 && as[0] == c) // 只有一個泛型參數,且該實現類型是該類型本身
27                         return c;
28                 }
29             }
30         }
31         return null;
32     }
33 
34     /**
35      * kc是k的類型,並且可以比較
36      */
37     @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
38     static int compareComparables(Class<?> kc, Object k, Object x) {
39         return (x == null || x.getClass() != kc ? 0 :
40                 ((Comparable)k).compareTo(x));
41     }
42 
43     /**
44      * 返回不小於cap的最小的2的整次冪
45      */
46     static final int tableSizeFor(int cap) {
47         int n = cap - 1;
48         n |= n >>> 1;
49         n |= n >>> 2;
50         n |= n >>> 4;
51         n |= n >>> 8;
52         n |= n >>> 16;
53         return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
54     }

7、迭代器

 1     /**
 2      * 這里我感覺設計的還挺好的,這里是編寫了一個Node節點的迭代器,這是一個抽象類,雖然是個抽象類,但是沒喲抽象方法
 3      * 留下了next方法去給子類實現,因為子類只有nest返回的東西是不同的
 4      */
 5     abstract class HashIterator {
 6         Node<K,V> next;        // next entry to return
 7         Node<K,V> current;     // current entry
 8         int expectedModCount;  // for fast-fail
 9         int index;             // current slot
10 
11         HashIterator() {
12             expectedModCount = modCount;
13             Node<K,V>[] t = table;
14             current = next = null;
15             index = 0;
16             if (t != null && size > 0) { // advance to first entry
17                 do {} while (index < t.length && (next = t[index++]) == null);
18             }
19         }
20 
21         public final boolean hasNext() {
22             return next != null;
23         }
24 
25         final Node<K,V> nextNode() {
26             Node<K,V>[] t;
27             Node<K,V> e = next;
28             //注意,這里在生成迭代器后,如果原來的圖不是通過迭代器進行對圖結構修改,那么就會報錯
29             if (modCount != expectedModCount)
30                 throw new ConcurrentModificationException();
31             if (e == null)
32                 throw new NoSuchElementException();
33             if ((next = (current = e).next) == null && (t = table) != null) {
34                 do {} while (index < t.length && (next = t[index++]) == null);
35             }
36             return e;
37         }
38 
39         public final void remove() {
40             Node<K,V> p = current;
41             if (p == null)
42                 throw new IllegalStateException();
43             //在不是通過remove修改之前,通過其他方式是不允許修改圖的結構的
44             if (modCount != expectedModCount)
45                 throw new ConcurrentModificationException();
46             current = null;
47             K key = p.key;
48             removeNode(hash(key), key, null, false, false);
49             expectedModCount = modCount;
50         }
51     }
52 
53     final class KeyIterator extends HashIterator
54         implements Iterator<K> {
55         public final K next() { return nextNode().key; }
56     }
57 
58     final class ValueIterator extends HashIterator
59         implements Iterator<V> {
60         public final V next() { return nextNode().value; }
61     }
62 
63     final class EntryIterator extends HashIterator
64         implements Iterator<Map.Entry<K,V>> {
65         public final Map.Entry<K,V> next() { return nextNode(); }
66     }

8、並行遍歷迭代器

  1     /**
  2      * jdk1.8發布后,對於並行處理的能力大大增強,Spliterator就是為了並行遍歷元素而設計的一個迭代器,
  3      * jdk1.8中的集合框架中的數據結構都默認實現了spliterator
  4      * 
  5      */
  6     static class HashMapSpliterator<K,V> {
  7         final HashMap<K,V> map;
  8         Node<K,V> current;          // 當前節點
  9         int index;                  // 當前節點下標,advance/split會修改這個值
 10         int fence;                  // 最后一個節點的下標,注意這里不是元素個數,而是數組下標
 11         int est;                    // 預測還有多少個元素
 12         int expectedModCount;       // 得到當前Map的結構修改次數
 13 
 14         HashMapSpliterator(HashMap<K,V> m, int origin,
 15                            int fence, int est,
 16                            int expectedModCount) {
 17             this.map = m;
 18             this.index = origin;
 19             this.fence = fence;
 20             this.est = est;
 21             this.expectedModCount = expectedModCount;
 22         }
 23 
 24         final int getFence() { // initialize fence and size on first use
 25             int hi;
 26             if ((hi = fence) < 0) {//當小於0的時候說明還沒有初始化
 27                 HashMap<K,V> m = map;
 28                 est = m.size;
 29                 expectedModCount = m.modCount;
 30                 Node<K,V>[] tab = m.table;
 31                 hi = fence = (tab == null) ? 0 : tab.length;//這里可以看到給出的是數組長度
 32             }
 33             return hi;
 34         }
 35 
 36         public final long estimateSize() {
 37             getFence(); // 這里是防止還沒有初始化的情況
 38             return (long) est;
 39         }
 40     }
 41 
 42     static final class KeySpliterator<K,V>
 43         extends HashMapSpliterator<K,V>
 44         implements Spliterator<K> {
 45         KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
 46                        int expectedModCount) {
 47             super(m, origin, fence, est, expectedModCount);
 48         }
 49 
 50         /**
 51          * 這個方法相當於把未遍歷的元素分成兩半,然后將前一半生成一個KeySpliterator,當前這個KeySpliterator
 52          * 處理后一半數據
 53          */
 54         public KeySpliterator<K,V> trySplit() {
 55             int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
 56             return (lo >= mid || current != null) ? null :
 57                 new KeySpliterator<>(map, lo, index = mid, est >>>= 1,//est >>>= 1的意思是est = est >>> 1
 58                                         expectedModCount);
 59         }
 60 
 61         /**
 62          * 這個方法就是通過action方法處理還沒有處理的元素
 63          */
 64         public void forEachRemaining(Consumer<? super K> action) {
 65             int i, hi, mc;
 66             if (action == null)
 67                 throw new NullPointerException();
 68             HashMap<K,V> m = map;
 69             Node<K,V>[] tab = m.table;
 70             if ((hi = fence) < 0) {//如果還沒有被初始化
 71                 mc = expectedModCount = m.modCount;
 72                 hi = fence = (tab == null) ? 0 : tab.length;
 73             }
 74             else
 75                 mc = expectedModCount;//這個mc就是為了在KeySpliterator調用的過程中確認沒有通過其他的方式改變Map的結構
 76             if (tab != null && tab.length >= hi &&
 77                 (i = index) >= 0 && (i < (index = hi) || current != null)) {
 78                 Node<K,V> p = current;
 79                 current = null;
 80                 do {
 81                     if (p == null)//這里是處理數組
 82                         p = tab[i++];
 83                     else {//這里是處理鏈表
 84                         action.accept(p.key);
 85                         p = p.next;
 86                     }
 87                 } while (p != null || i < hi);
 88                 if (m.modCount != mc)
 89                     throw new ConcurrentModificationException();
 90             }
 91         }
 92 
 93         //單個對元素執行給定的動作,如果有剩下元素未處理返回true,否則返回false
 94         public boolean tryAdvance(Consumer<? super K> action) {
 95             int hi;
 96             if (action == null)
 97                 throw new NullPointerException();
 98             Node<K,V>[] tab = map.table;
 99             if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
100                 while (current != null || index < hi) {
101                     if (current == null)
102                         current = tab[index++];
103                     else {
104                         K k = current.key;
105                         current = current.next;
106                         action.accept(k);
107                         if (map.modCount != expectedModCount)
108                             throw new ConcurrentModificationException();
109                         return true;
110                     }
111                 }
112             }
113             return false;
114         }
115 
116         //返回當前對象有哪些特征值
117         public int characteristics() {
118             return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
119                 Spliterator.DISTINCT;
120         }
121     }
122 
123     //下面的就不再贅述,只是用方法處理的對象不一樣,其他的都和上面一樣
124     static final class ValueSpliterator<K,V>
125         extends HashMapSpliterator<K,V>
126         implements Spliterator<V> {
127         ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
128                          int expectedModCount) {
129             super(m, origin, fence, est, expectedModCount);
130         }
131 
132         public ValueSpliterator<K,V> trySplit() {
133             int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
134             return (lo >= mid || current != null) ? null :
135                 new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
136                                           expectedModCount);
137         }
138 
139         public void forEachRemaining(Consumer<? super V> action) {
140             int i, hi, mc;
141             if (action == null)
142                 throw new NullPointerException();
143             HashMap<K,V> m = map;
144             Node<K,V>[] tab = m.table;
145             if ((hi = fence) < 0) {
146                 mc = expectedModCount = m.modCount;
147                 hi = fence = (tab == null) ? 0 : tab.length;
148             }
149             else
150                 mc = expectedModCount;
151             if (tab != null && tab.length >= hi &&
152                 (i = index) >= 0 && (i < (index = hi) || current != null)) {
153                 Node<K,V> p = current;
154                 current = null;
155                 do {
156                     if (p == null)
157                         p = tab[i++];
158                     else {
159                         action.accept(p.value);
160                         p = p.next;
161                     }
162                 } while (p != null || i < hi);
163                 if (m.modCount != mc)
164                     throw new ConcurrentModificationException();
165             }
166         }
167 
168         public boolean tryAdvance(Consumer<? super V> action) {
169             int hi;
170             if (action == null)
171                 throw new NullPointerException();
172             Node<K,V>[] tab = map.table;
173             if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
174                 while (current != null || index < hi) {
175                     if (current == null)
176                         current = tab[index++];
177                     else {
178                         V v = current.value;
179                         current = current.next;
180                         action.accept(v);
181                         if (map.modCount != expectedModCount)
182                             throw new ConcurrentModificationException();
183                         return true;
184                     }
185                 }
186             }
187             return false;
188         }
189 
190         public int characteristics() {
191             return (fence < 0 || est == map.size ? Spliterator.SIZED : 0);
192         }
193     }
194 
195     static final class EntrySpliterator<K,V>
196         extends HashMapSpliterator<K,V>
197         implements Spliterator<Map.Entry<K,V>> {
198         EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est,
199                          int expectedModCount) {
200             super(m, origin, fence, est, expectedModCount);
201         }
202 
203         public EntrySpliterator<K,V> trySplit() {
204             int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
205             return (lo >= mid || current != null) ? null :
206                 new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
207                                           expectedModCount);
208         }
209 
210         public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
211             int i, hi, mc;
212             if (action == null)
213                 throw new NullPointerException();
214             HashMap<K,V> m = map;
215             Node<K,V>[] tab = m.table;
216             if ((hi = fence) < 0) {
217                 mc = expectedModCount = m.modCount;
218                 hi = fence = (tab == null) ? 0 : tab.length;
219             }
220             else
221                 mc = expectedModCount;
222             if (tab != null && tab.length >= hi &&
223                 (i = index) >= 0 && (i < (index = hi) || current != null)) {
224                 Node<K,V> p = current;
225                 current = null;
226                 do {
227                     if (p == null)
228                         p = tab[i++];
229                     else {
230                         action.accept(p);
231                         p = p.next;
232                     }
233                 } while (p != null || i < hi);
234                 if (m.modCount != mc)
235                     throw new ConcurrentModificationException();
236             }
237         }
238 
239         public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
240             int hi;
241             if (action == null)
242                 throw new NullPointerException();
243             Node<K,V>[] tab = map.table;
244             if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
245                 while (current != null || index < hi) {
246                     if (current == null)
247                         current = tab[index++];
248                     else {
249                         Node<K,V> e = current;
250                         current = current.next;
251                         action.accept(e);
252                         if (map.modCount != expectedModCount)
253                             throw new ConcurrentModificationException();
254                         return true;
255                     }
256                 }
257             }
258             return false;
259         }
260 
261         public int characteristics() {
262             return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
263                 Spliterator.DISTINCT;
264         }
265     }

9、JDK1.8新增的方法部分

  1     @Override
  2     public V getOrDefault(Object key, V defaultValue) {//如果沒有key的情況下會返回defaultValue
  3         Node<K,V> e;
  4         return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
  5     }
  6 
  7     @Override
  8     public V putIfAbsent(K key, V value) {//當存在key的時候,不會用value去覆蓋
  9         return putVal(hash(key), key, value, true, true);
 10     }
 11 
 12     @Override
 13     public boolean remove(Object key, Object value) {//當key和value都相同時,才去刪除這個值
 14         return removeNode(hash(key), key, value, true, true) != null;
 15     }
 16 
 17     @Override
 18     public boolean replace(K key, V oldValue, V newValue) {//當key和oldValue都相同時,用newValue去代替oldValue
 19         Node<K,V> e; V v;
 20         if ((e = getNode(hash(key), key)) != null &&
 21             ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
 22             e.value = newValue;
 23             afterNodeAccess(e);
 24             return true;
 25         }
 26         return false;
 27     }
 28 
 29     @Override
 30     public V replace(K key, V value) {//這個和上面方法不同在於,不用確定舊的值,直接覆蓋,並且返回oldValue
 31         Node<K,V> e;
 32         if ((e = getNode(hash(key), key)) != null) {
 33             V oldValue = e.value;
 34             e.value = value;
 35             afterNodeAccess(e);
 36             return oldValue;
 37         }
 38         return null;
 39     }
 40 
 41     /**
 42      * 這個方法就類似於get方法,但是不同之處在於當key不存在時不時返回null,而是通過mappingFunction計算出一個值返回
 43      */
 44     @Override
 45     public V computeIfAbsent(K key,
 46                              Function<? super K, ? extends V> mappingFunction) {
 47         if (mappingFunction == null)
 48             throw new NullPointerException();
 49         int hash = hash(key);
 50         Node<K,V>[] tab; Node<K,V> first; int n, i;
 51         int binCount = 0;
 52         TreeNode<K,V> t = null;
 53         Node<K,V> old = null;
 54         if (size > threshold || (tab = table) == null ||
 55             (n = tab.length) == 0)
 56             n = (tab = resize()).length;
 57         if ((first = tab[i = (n - 1) & hash]) != null) {
 58             if (first instanceof TreeNode)
 59                 old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
 60             else {
 61                 Node<K,V> e = first; K k;
 62                 do {
 63                     if (e.hash == hash &&
 64                         ((k = e.key) == key || (key != null && key.equals(k)))) {
 65                         old = e;
 66                         break;
 67                     }
 68                     ++binCount;
 69                 } while ((e = e.next) != null);
 70             }
 71             V oldValue;
 72             if (old != null && (oldValue = old.value) != null) {
 73                 afterNodeAccess(old);
 74                 return oldValue;
 75             }
 76         }
 77         V v = mappingFunction.apply(key);
 78         if (v == null) {
 79             return null;
 80         } else if (old != null) {
 81             old.value = v;
 82             afterNodeAccess(old);
 83             return v;
 84         }
 85         else if (t != null)
 86             t.putTreeVal(this, tab, hash, key, v);
 87         else {
 88             tab[i] = newNode(hash, key, v, first);
 89             if (binCount >= TREEIFY_THRESHOLD - 1)
 90                 treeifyBin(tab, hash);
 91         }
 92         ++modCount;
 93         ++size;
 94         afterNodeInsertion(true);
 95         return v;
 96     }
 97 
 98     //這個方法和尚一個方法不同的是,這個方法在key存在時,通過key和value算出一個新的value返回,如果不存在,返回null
 99     public V computeIfPresent(K key,
100                               BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
101         if (remappingFunction == null)
102             throw new NullPointerException();
103         Node<K,V> e; V oldValue;
104         int hash = hash(key);
105         if ((e = getNode(hash, key)) != null &&
106             (oldValue = e.value) != null) {
107             V v = remappingFunction.apply(key, oldValue);
108             if (v != null) {
109                 e.value = v;
110                 afterNodeAccess(e);
111                 return v;
112             }
113             else
114                 removeNode(hash, key, null, false, true);
115         }
116         return null;
117     }
118 
119     @Override
120     //這個方法綜合了上面的兩個方法,都會帶入remappingFunction進行運算新值,並且替換舊值
121     public V compute(K key,
122                      BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
123         if (remappingFunction == null)
124             throw new NullPointerException();
125         int hash = hash(key);
126         Node<K,V>[] tab; Node<K,V> first; int n, i;
127         int binCount = 0;
128         TreeNode<K,V> t = null;
129         Node<K,V> old = null;
130         if (size > threshold || (tab = table) == null ||
131             (n = tab.length) == 0)
132             n = (tab = resize()).length;
133         if ((first = tab[i = (n - 1) & hash]) != null) {
134             if (first instanceof TreeNode)
135                 old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
136             else {
137                 Node<K,V> e = first; K k;
138                 do {
139                     if (e.hash == hash &&
140                         ((k = e.key) == key || (key != null && key.equals(k)))) {
141                         old = e;
142                         break;
143                     }
144                     ++binCount;
145                 } while ((e = e.next) != null);
146             }
147         }
148         V oldValue = (old == null) ? null : old.value;
149         V v = remappingFunction.apply(key, oldValue);
150         if (old != null) {
151             if (v != null) {
152                 old.value = v;
153                 afterNodeAccess(old);
154             }
155             else
156                 removeNode(hash, key, null, false, true);
157         }
158         else if (v != null) {
159             if (t != null)
160                 t.putTreeVal(this, tab, hash, key, v);
161             else {
162                 tab[i] = newNode(hash, key, v, first);
163                 if (binCount >= TREEIFY_THRESHOLD - 1)
164                     treeifyBin(tab, hash);
165             }
166             ++modCount;
167             ++size;
168             afterNodeInsertion(true);
169         }
170         return v;
171     }
172 
173     @Override
174     //這個函數就是將oldVAlue和value通過remappingFunction進行一下混合,然后代替舊值
175     public V merge(K key, V value,
176                    BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
177         if (value == null)
178             throw new NullPointerException();
179         if (remappingFunction == null)
180             throw new NullPointerException();
181         int hash = hash(key);
182         Node<K,V>[] tab; Node<K,V> first; int n, i;
183         int binCount = 0;
184         TreeNode<K,V> t = null;
185         Node<K,V> old = null;
186         if (size > threshold || (tab = table) == null ||
187             (n = tab.length) == 0)
188             n = (tab = resize()).length;
189         if ((first = tab[i = (n - 1) & hash]) != null) {
190             if (first instanceof TreeNode)
191                 old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
192             else {
193                 Node<K,V> e = first; K k;
194                 do {
195                     if (e.hash == hash &&
196                         ((k = e.key) == key || (key != null && key.equals(k)))) {
197                         old = e;
198                         break;
199                     }
200                     ++binCount;
201                 } while ((e = e.next) != null);
202             }
203         }
204         if (old != null) {
205             V v;
206             if (old.value != null)
207                 v = remappingFunction.apply(old.value, value);
208             else
209                 v = value;
210             if (v != null) {
211                 old.value = v;
212                 afterNodeAccess(old);
213             }
214             else
215                 removeNode(hash, key, null, false, true);
216             return v;
217         }
218         if (value != null) {
219             if (t != null)
220                 t.putTreeVal(this, tab, hash, key, value);
221             else {
222                 tab[i] = newNode(hash, key, value, first);
223                 if (binCount >= TREEIFY_THRESHOLD - 1)
224                     treeifyBin(tab, hash);
225             }
226             ++modCount;
227             ++size;
228             afterNodeInsertion(true);
229         }
230         return value;
231     }
232 
233     @Override
234     //對於Map中的每一項,進行action運算
235     public void forEach(BiConsumer<? super K, ? super V> action) {
236         Node<K,V>[] tab;
237         if (action == null)
238             throw new NullPointerException();
239         if (size > 0 && (tab = table) != null) {
240             int mc = modCount;
241             for (int i = 0; i < tab.length; ++i) {
242                 for (Node<K,V> e = tab[i]; e != null; e = e.next)
243                     action.accept(e.key, e.value);
244             }
245             if (modCount != mc)
246                 throw new ConcurrentModificationException();
247         }
248     }
249 
250     @Override
251     //Map中所有的值,都會被function(key,value)替換為新值
252     public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
253         Node<K,V>[] tab;
254         if (function == null)
255             throw new NullPointerException();
256         if (size > 0 && (tab = table) != null) {
257             int mc = modCount;
258             for (int i = 0; i < tab.length; ++i) {
259                 for (Node<K,V> e = tab[i]; e != null; e = e.next) {
260                     e.value = function.apply(e.key, e.value);
261                 }
262             }
263             if (modCount != mc)
264                 throw new ConcurrentModificationException();
265         }
266     }

三、HashMap的非線程安全(見博客https://www.cnblogs.com/softidea/p/7261111.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM