前言:
我們知道 HashMap 集合是允許存放 null 值的。
hashMap 是根據 key 的 hashCode 來尋找存放位置的,那當 key 為null時, 問題來了 它怎么就可以存儲 null 值了那?
特此提筆記錄:源碼瞧一瞧就一目了然了
HashMap 允許插入鍵為 null 的鍵值對。
但是因為無法調用 null 的 hashCode() 方法,也就無法確定該鍵值對的桶下標,只能通過強制指定一個桶下標來存放。
HashMap 使用第 0 個桶存放鍵為 null 的鍵值對。
JDK1.6:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; } private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; }
JDK1.7:
// 將“key-value”添加到HashMap中 public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null)// 若“key為null”,則將該鍵值對添加到table[0]中。 return putForNullKey(value); // 若“key不為null”,則計算該key的哈希值,然后將其添加到該哈希值對應的鏈表中。 int hash = hash(key);//獲取key的hash值 int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 若“該key”對應的鍵值對已經存在,則用新的value取代舊的value。然后退出! V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 若“key”對應的鍵值對不存在,則將“key-value”添加到table中 modCount++; //將key-value添加到table[i]處 addEntry(hash, key, value, i); return null; } // putForNullKey()的作用是將“key為null”鍵值對添加到table[0]位置 private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 如果沒有存在key為null的鍵值對,則直接添加到table[0]處! modCount++; addEntry(0, null, value, 0); return null; }
當 HashMap 的 put 方法,第二個判斷就是 key 為 null 的判斷后進入putForNullKey(V value)這個方法
可以看到,前面那個 for 循環,是在 talbe[0] 鏈表中查找 key 為 null 的元素,如果找到,則將 value 重新賦值給這個元素的 value,並返回原來的 value。
如果上面for循環沒找到則將這個元素添加到talbe[0]鏈表的表頭。
JDK1.8:
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }