接着上一篇繼續
1 /** 2 * 返回map的長度 3 * @return the number of key-value mappings in this map 4 */ 5 public int size() { 6 return size; 7 } 8 9 /** 10 *如果map的長度爲0 則返回true 11 * @return <tt>true</tt> if this map contains no key-value mappings 12 */ 13 public boolean isEmpty() { 14 return size == 0; 15 } 16 17 /** 18 * map的key 與value 都可以爲 null 19 * @see #put(Object, Object) 20 */ 21 public V get(Object key) { 22 Node<K,V> e; 23 return (e = getNode(hash(key), key)) == null ? null : e.value; 24 } 25 26 /** 27 * Implements Map.get and related methods 28 *通過key值的hash值 與key 去查找Node 29 * @param hash hash for key 30 * @param key the key 31 * @return the node, or null if none 32 */ 33 final Node<K,V> getNode(int hash, Object key) { 34 Node<K,V>[] tab; Node<K,V> first, e; int n; K k; 35 if ((tab = table) != null && (n = tab.length) > 0 && 36 (first = tab[(n - 1) & hash]) != null) {//將table 賦值給tab table不是 null ,table 長度大於0 該hash找到的hash桶Node不爲null 37 if (first.hash == hash && // always check first node//如果是一個元素直接判斷 38 ((k = first.key) == key || (key != null && key.equals(k))))//判斷找到的桶的hash值是否等於當前要找的hash值,key值是否是自己要找的key 39 return first; 40 if ((e = first.next) != null) {//判斷該哈希值是否有下一個元素 41 if (first instanceof TreeNode)//判斷該hash桶是否是紅黑樹 42 return ((TreeNode<K,V>)first).getTreeNode(hash, key);//如果是紅黑樹已紅黑樹的方式查找 43 do {//不是紅黑樹就是鏈表,以鏈表的方式遍歷查找。 44 if (e.hash == hash && 45 ((k = e.key) == key || (key != null && key.equals(k)))) 46 return e; 47 } while ((e = e.next) != null); 48 } 49 } 50 return null; 51 } 52 53 /** 54 * 判斷是否包含某個key ,就是一個get 操作 55 * @param key The key whose presence in this map is to be tested 56 * @return <tt>true</tt> if this map contains a mapping for the specified 57 * key. 58 */ 59 public boolean containsKey(Object key) { 60 return getNode(hash(key), key) != null; 61 } 62 63 /** 64 * 將指定值與該映射中的指定鍵相關聯。 65 * 如果該映射先前包含該鍵的映射,則舊 66 * 值被替換。 67 * 68 * @param key key with which the specified value is to be associated 69 * @param value value to be associated with the specified key 70 * @return the previous value associated with <tt>key</tt>, or 71 * <tt>null</tt> if there was no mapping for <tt>key</tt>. 72 * (A <tt>null</tt> return can also indicate that the map 73 * previously associated <tt>null</tt> with <tt>key</tt>.) 74 */ 75 public V put(K key, V value) { 76 return putVal(hash(key), key, value, false, true); 77 } 78 79 /** 80 * Implements Map.put and related methods 81 * 82 * @param hash hash for key 83 * @param key the key 84 * @param value the value to put 85 * @param onlyIfAbsent 如果 true, 不能修改覆蓋當前的值 86 * @param evict 如果 false, 當前table正在創建 87 * @return previous value, or null if none 88 */ 89 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 90 boolean evict) { 91 Node<K,V>[] tab; Node<K,V> p; int n, i; 92 if ((tab = table) == null || (n = tab.length) == 0)//如果table 沒有初始化 93 n = (tab = resize()).length;//擴容操作 94 if ((p = tab[i = (n - 1) & hash]) == null)// (n - 1) & hash 映射到一個hash桶賦值給p ,n的值爲2的冪 轉爲二進制就是1000..減1 後就是11111111... 95 //這樣與hash進行& 操作時,就是111111111...與hash 之間 相同位置都爲1纔是1,這樣就可以均勻分佈到了hash桶中。 96 tab[i] = newNode(hash, key, value, null);//創建第一個hash桶 97 else { 98 Node<K,V> e; K k; 99 if (p.hash == hash && 100 ((k = p.key) == key || (key != null && key.equals(k))))//判斷p 這個hash桶的hash值是否==當前的key 101 e = p; 102 else if (p instanceof TreeNode)//如果hash桶是紅黑樹 103 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//添加到樹 104 else {//是鏈表 105 for (int binCount = 0; ; ++binCount) { 106 if ((e = p.next) == null) {//循環判斷桶中是否有下一個元素 107 p.next = newNode(hash, key, value, null); 108 if (binCount >= TREEIFY_THRESHOLD - 1) //判斷是否達到轉換爲紅黑樹的閾值 109 treeifyBin(tab, hash);//轉換爲紅黑樹 110 break; 111 } 112 if (e.hash == hash && 113 ((k = e.key) == key || (key != null && key.equals(k))))//如果鏈表中有該key值,則跳出 114 break; 115 p = e; 116 } 117 } 118 if (e != null) { // existing mapping for key 119 V oldValue = e.value;//把e的值賦值給 oldValue 120 if (!onlyIfAbsent || oldValue == null)//如果onlyIfAbsent false 表示可以覆蓋 121 e.value = value; 122 afterNodeAccess(e); 123 return oldValue; 124 } 125 } 126 ++modCount;//操作數加1 ,相當於加了一個版本號 127 if (++size > threshold) //當size大於容器的擴容閾值 128 resize();//開始擴容 129 afterNodeInsertion(evict);//給LinkHashMap 使用的,在HashMap中是一個空方法 130 return null; 131 } 132 133 /** 134 *HashMap的擴容操作,需要進行 135 * @return the table 136 */ 137 final Node<K,V>[] resize() { 138 Node<K,V>[] oldTab = table;//把當前的table 容器備份給oldTab 139 int oldCap = (oldTab == null) ? 0 : oldTab.length;//計算容器的hash桶的數量 140 int oldThr = threshold;//把當前的容器擴容閾值記錄 141 int newCap, newThr = 0;//初始化要創建的新生的容器的hash桶的數量與擴容閾值 142 if (oldCap > 0) { //表示當前容器不是初生的容器 143 if (oldCap >= MAXIMUM_CAPACITY) {//如果當前的hash桶的數量大於最大容量 144 threshold = Integer.MAX_VALUE;//容器擴容閾值=最大的值 145 return oldTab; 146 } 147 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 148 oldCap >= DEFAULT_INITIAL_CAPACITY) //如果當前的容器大小擴大1倍小於最大容量 或當前的容器的大小大於或等於初始化大小 149 newThr = oldThr << 1; // double threshold 新的容器的擴容閾值是當前容器的2倍 150 } 151 //容器初次創建 152 else if (oldThr > 0) //當前的容器擴容閾值>0 153 newCap = oldThr;//容器的擴容閾值=當前的容器擴容閾值 154 else { // 使用默認的設置 155 newCap = DEFAULT_INITIAL_CAPACITY; 156 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 157 } 158 159 //==================== 160 if (newThr == 0) {//如果以上的操作後新的擴容閾值依然==0 161 float ft = (float)newCap * loadFactor; //計算新的擴容閾值 162 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 163 (int)ft : Integer.MAX_VALUE); //新的容器大小 小於最大容量值並且 新計算的ft < 最大容量值 .... 164 } 165 //開始幹正事兒了..... 166 //擴容開始...~~~ 167 threshold = newThr;//賦值容器擴容閾值 168 @SuppressWarnings({"rawtypes","unchecked"}) 169 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; 170 table = newTab;//賦值新容器 171 if (oldTab != null) {//檢查舊容器是否爲空 172 for (int j = 0; j < oldCap; ++j) {//遍歷舊的hash桶 173 Node<K,V> e; 174 if ((e = oldTab[j]) != null) {//判斷hash桶中有數據 ,並備份 175 oldTab[j] = null; 176 if (e.next == null)//判斷是否是普通數據 177 newTab[e.hash & (newCap - 1)] = e;//放入新的hash桶中 178 else if (e instanceof TreeNode)//判斷是否是紅黑樹 179 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);//樹重新放入hash桶 180 else { // 剩下的一定是鏈表,用來解決jdk7中出現的擴容拷貝環問題。 181 Node<K,V> loHead = null, loTail = null;// 低 定義頭尾 182 Node<K,V> hiHead = null, hiTail = null;// 高 定義頭尾 183 Node<K,V> next; 184 /** 185 * oldCap一定是2的整數次冪, 這裏假設是2^m 要麼是0 要麼是2^m 186 * newCap是oldCap的兩倍, 則會是2^(m+1) 187 * 那麼進行與操作的時候是什麼樣的呢? 188 * 例如:oldCap=8 ,e.hash=5(這個值隨意) 189 * 換爲二進制爲 : 190 * oldCap:1000 191 * e.hash:0101 192 * 進行&操作會怎麼樣呢? 得到值爲 oldCap 或 0, 193 * 爲什麼會這樣? 因爲 oldCap只會在最高位上一位爲 1 那結果只能是這兩種情況 194 * 那爲什麼要用這種方式來把原鏈表中的數據分成兩個鏈表呢? 195 * 那就分析一下HashMap的Hash 規則 (n - 1) & hash ,其中 n 就是 newCap 的擴容閾值 也就是會是 2^(m+1) 196 * 舉個例子: 197 * 當前的二進制分別爲 198 * oldCap: 01000 199 * oldCap-1: 00111 200 * newCap: 10000 201 * newCap-1: 01111 202 * e.hash: 00101 203 * 204 * 也就是說在 e.hash 沒有改變的情況下 ,新的newCap-1比 oldCap-1 多了最高一位 1 那進行hash 計算後的情況 就只有兩種 , 205 * 1、跟原來的hash相同 (這樣就會原位不動) 206 * 2、在新的容器中高位上多了一個1,那就是多了oldCap一倍 (這樣就在原位置上乘以2) 207 * 208 * 那再分析一下這個結論跟使用 (e.hash & oldCap) == 0 的關係,那就是==0時, e.hash的關鍵一位(newCap-1比 oldCap-1 多了最高一位 1)是 0,而最高一位不是0的一定是1 209 * 好了,分析完畢。 210 */ 211 do { 212 next = e.next;//備份 213 if ((e.hash & oldCap) == 0) {//判斷如果==0 不移位 214 if (loTail == null)//放到低位鏈表 215 loHead = e; 216 else 217 loTail.next = e; 218 loTail = e; 219 } 220 else {//否則 移位到高位 221 if (hiTail == null)//放到高位鏈表 222 hiHead = e; 223 else 224 hiTail.next = e; 225 hiTail = e; 226 } 227 } while ((e = next) != null); 228 if (loTail != null) { 229 loTail.next = null; 230 newTab[j] = loHead;//原位不動 231 } 232 if (hiTail != null) { 233 hiTail.next = null; 234 newTab[j + oldCap] = hiHead;//位置加oldCap一倍 235 } 236 } 237 } 238 } 239 } 240 return newTab; 241 }
超詳細的HashMap 源碼解讀