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)