記錄小白實習生的HashMap原始碼 put元素 的學習和一些疑問
首先看HashMap儲存結構
transient Node<K,V>[] table; static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key;this.value = value; this.next = next; } /* …… */ }
我對儲存結構的理解
初始化一個長度為16的Node陣列,陣列中每一個元素是一個Node構成的單鏈表,好像是大家說的桶?當桶中Node結點長度(連結串列長度)大於等於TREEIFY_THRESHOLD (8)時 單鏈表改為 樹(紅黑樹?現在還一點不瞭解,知道個名字) 儲存 ,當桶中Node結點長度(連結串列長度)小於等於UNTREEIFY_THRESHOLD (6)時,樹形結構轉換為單鏈表儲存
public V put(K key, V value) {return putVal(hash(key), key, value, false, true); }
putVal 引數 分別為 key的雜湊值,key值,value值,onlyIfAbsent true表示只有在該key對應原來的value為null的時候才插入,也就是說如果value之前存在了,就不會被新put的元素覆蓋,false相反,evict //evict if false, the table is in creation mode. 這個是原始碼中的註釋,true的話就不是creation mode?看園裡大佬zju_jzb的博說
用於LinkedHashMap中的尾部操作,這裡沒有實際意義 傳送門https://www.cnblogs.com/jzb-blog/p/6637823.html
new HashMap<>()進行put時
先對table=null 和 table.length = 0 的情況進行處理 執行resize方法預設構造一個長為16的Node陣列
再根據hash值 (table.length - 1) & hash 計算出put 的桶的下標
若該元素為空 newNode(hash, key, value, null); 建立一個單鏈表的“頭”結點
若該元素不為空
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p;
如果第一個元素key與put的key相同時,將第一個元素引用賦值給要put的新結點e
else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
如果第一個元素 是 TreeNode型別時,說明已轉換為樹形結構儲存,插入到樹中
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; } }
如果第一個元素與要put元素不同,而且此時也仍是單鏈表結構儲存的話,遍歷連結串列。找到後又分三種情況
第一種情況時,插入後連結串列長度達到8,需要轉化為樹形結構。
第二種情況時,插入後連結串列長度小於8,仍然是連結串列儲存。
第三種情況時,連結串列中遍歷到相同key值的結點,獲得該結點的引用
if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } }
對已存在key的value進行覆蓋 返回put之前key所對應的值
++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null;
modCount是對HashMap結構改變次數的記錄(插入刪除)
若put一個元素後 需要對Node[] table進行擴容 就擴容
putVal 方法走到這裡時已經說明 HashMap中不存在和要put的Key相同的Key 返回null
afterNodeInsertion方法還不瞭解
看了一天了 感覺對HashMap的大致結構有一定的瞭解了 但是還有很多疑問 很多高深的東東 這次算入園了 希望各位大佬多多照顧小弟
希望能在java路上越走越遠