首先說一下HashMap存儲結構,數組、鏈表、樹這三種數據結構形成了hashMap。
存儲結構下圖所示,根據key的hash與table長度確定table位置,同一個位置的key以鏈表形式存儲,超過一定限制鏈表轉為樹。
數組的具體存取規則是tab[(n-1) & hash],其中tab為node數組,n為數組的長度,hash為key的hash值。
//鏈表中數據的臨界值,如果達到8,就進行resize擴展,如果數組大於64則轉換為樹.
static final int TREEIFY_THRESHOLD = 8;
//如果鏈表的數據小於6,則從樹轉換為鏈表.
static final int UNTREEIFY_THRESHOLD = 6;
//如果數組的size大於64,則把鏈表進行轉化為樹
static final int MIN_TREEIFY_CAPACITY = 64
//根據key匹配Node,如果匹配不到key,則返回defaultValue
@Override public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; }
//根據key匹配Node,如果匹配不到則增加key-value,返回null,如果匹配到Node,如果oldValue不等於null則不進行value覆蓋,返回oldValue
@Override public V putIfAbsent(K key, V value) { return putVal(hash(key), key, value, true, true); }
//根據key匹配node,如果value也相同則刪除
@Override public boolean remove(Object key, Object value) { return removeNode(hash(key), key, value, true, true) != null; }
//根據key匹配node,如果value也相同則使用newValue覆蓋返回true,否則返回false
@Override public boolean replace(K key, V oldValue, V newValue) { Node<K,V> e; V v; if ((e = getNode(hash(key), key)) != null && ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) { e.value = newValue; afterNodeAccess(e); return true; } return false; }
//根據key做匹配Node,(匹配不到則新建然后重排)如果Node有value,則直接返回oldValue,如果沒有value則根據Function接口的apply方法獲取value,返回value。
//Function接口的apply的入參為key,調用computeIfAbsent時重寫Function接口可以根據key進行邏輯處理,apply的返回值即為要存儲的value。
@Override public V computeIfAbsent(K key, Functionsuper K, ? extends V> mappingFunction) { if (mappingFunction == null) throw new NullPointerException(); int hash = hash(key); Node<K,V>[] tab; Node<K,V> first; int n, i; int binCount = 0; TreeNode<K,V> t = null; Node<K,V> old = null; if (size > threshold || (tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { //如果已經轉為樹,按照樹的規則進行處理 if (first instanceof TreeNode) old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key); else { Node<K,V> e = first; K k; //查找整個鏈表,找到對應的key do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++binCount; } while ((e = e.next) != null); } V oldValue; if (old != null && (oldValue = old.value) != null) { afterNodeAccess(old); return oldValue; } } //根據重寫邏輯計算返回value V v = mappingFunction.apply(key); if (v == null) { return null; } else if (old != null) { old.value = v; afterNodeAccess(old); return v; } else if (t != null) t.putTreeVal(this, tab, hash, key, v); else { //如果匹配不到則table加入數據 tab[i] = newNode(hash, key, v, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); return v; }
//V computeIfPresent(K key,BiFunction remappingFunction):根據key做匹配,如果匹配不上則返回null,匹配上根據BiFunction的apply方法獲取value,返回value。BiFunction接口的apply的入參為key、oldValue,調用computeIfPresent時重寫Function接口可以根據key和oldValue進行邏輯處理,apply的返回值如果為null則刪除該節點,否則即為要存儲的value。
public V computeIfPresent(K key, BiFunctionsuper K, ? super V, ? extends V> remappingFunction) { if (remappingFunction == null) throw new NullPointerException(); Node<K,V> e; V oldValue; int hash = hash(key); if ((e = getNode(hash, key)) != null && (oldValue = e.value) != null) { //使用key和原value作為入參 V v = remappingFunction.apply(key, oldValue); if (v != null) { e.value = v; afterNodeAccess(e); return v; } else removeNode(hash, key, null, false, true); } return null; }
//V compute(K key,BiFunction remappingFunction):根據key做匹配,根據BiFunction的apply返回做存儲的value。匹配到Node做value替換,匹配不到新增node。apply的返回值如果為null則刪除該節點,否則即為要存儲的value。
@Override public V compute(K key, BiFunctionsuper K, ? super V, ? extends V> remappingFunction) { if (remappingFunction == null) throw new NullPointerException(); int hash = hash(key); Node<K,V>[] tab; Node<K,V> first; int n, i; int binCount = 0; TreeNode<K,V> t = null; Node<K,V> old = null; if (size > threshold || (tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof TreeNode) old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key); else { Node<K,V> e = first; K k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++binCount; } while ((e = e.next) != null); } } V oldValue = (old == null) ? null : old.value; //使用key和原value作為入參 V v = remappingFunction.apply(key, oldValue); if (old != null) { if (v != null) { old.value = v; afterNodeAccess(old); } else removeNode(hash, key, null, false, true); } else if (v != null) { if (t != null) t.putTreeVal(this, tab, hash, key, v); else { tab[i] = newNode(hash, key, v, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); } return v; }
// V merge(K key, V value,BiFunction remappingFunction):功能大部分與compute相同,不同之處在於BiFunction中apply的參數,入參為oldValue、value,調用merge時根據兩個value進行邏輯處理並返回value。
@Override public V merge(K key, V value, BiFunctionsuper V, ? super V, ? extends V> remappingFunction) { if (value == null) throw new NullPointerException(); if (remappingFunction == null) throw new NullPointerException(); int hash = hash(key); Node<K,V>[] tab; Node<K,V> first; int n, i; int binCount = 0; TreeNode<K,V> t = null; Node<K,V> old = null; if (size > threshold || (tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof TreeNode) old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key); else { Node<K,V> e = first; K k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++binCount; } while ((e = e.next) != null); } } if (old != null) { V v; if (old.value != null) //使用新老value作為入參 v = remappingFunction.apply(old.value, value); else v = value; if (v != null) { old.value = v; afterNodeAccess(old); } else removeNode(hash, key, null, false, true); return v; } if (value != null) { if (t != null) t.putTreeVal(this, tab, hash, key, value); else { tab[i] = newNode(hash, key, value, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); } return value; }
// void forEach(BiConsumer action):調用此方法時實現BiConsumer接口重寫void accept(Object o, Object o2)方法,其中o為key,o2為value,可根據自己的實現對map中所有數據進行處理。
@Override public void forEach(BiConsumersuper K, ? super V> action) { Node<K,V>[] tab; if (action == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (int i = 0; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null; e = e.next) action.accept(e.key, e.value); } if (modCount != mc) throw new ConcurrentModificationException(); } }
// void replaceAll(BiFunction function):調用此方法時重寫BiFunction的Object apply(Object o, Object o2)方法,其中o為key,o2為value,根據重寫方法邏輯進行重新賦值。
@Override public void replaceAll(BiFunctionsuper K, ? super V, ? extends V> function) { Node<K,V>[] tab; if (function == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (int i = 0; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null; e = e.next) { e.value = function.apply(e.key, e.value); } } if (modCount != mc) throw new ConcurrentModificationException(); } }
computeIfAbsent、computeIfPresent、compute對比
computeIfAbsent:如果key已存在,返回oldVlaue;不存在創建,返回新創建value
computeIfPresent:如果key不存在,返回null;如果已存在,value為null則刪除此節點,不為null替換節點value並返回此value。
compute:如果key不存在,新建key進行存儲;如果key存在,value為null則刪除此節點,不為null替換節點value並返回此value。