Java8 Map里新增了幾個方法,很多同學不知道具體功能是啥。
先從最簡單的開始。
putIfAbsent()
HashMap<String, String> map = new HashMap<String, String>();
map.putIfAbsent("k", "v");
等價于:(功能等價刻帚,效率并不等價)
HashMap<String, String> map = new HashMap<String, String>();
if(!map.containsKey("k")) {
map.put("k", "v");
}
key不存在才put,存在就跳過涩嚣。
源碼:
@Override
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
...// 省略一部分無關代碼
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null) // 直接看這里
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
...// 繼續(xù)省略一部分代碼
}
可以看到崇众,putIfAbsent()
調用了putVal()
掂僵,第四個參數onlyIfAbsent
傳true,表示只有無此key時才put顷歌。如果putIfAbsent()
的key不存在锰蓬,和put()
邏輯相同。如果已存在眯漩,onlyIfAbsent
延遲到putVal()
的 if (e != null)
時才做判斷芹扭。
merge()
String k = "key";
HashMap<String, Integer> map = new HashMap<String, Integer>() {{
put(k, 1);
}};
map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);
等價于:
String k = "key";
HashMap<String, Integer> map = new HashMap<String, Integer>() {{
put(k, 1);
}};
Integer newVal = 2;
if(map.containsKey(k)) {
map.put(k, map.get(k) + newVal);
} else {
map.put(k, newVal);
}
如果key存在,則執(zhí)行l(wèi)ambda表達式赦抖,表達式入參為oldVal
和newVal
(neVal即merge()
的第二個參數)舱卡。表達式返回最終put的val。如果key不存在队萤,則直接putnewVal
轮锥。
源碼:
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
···// 刪除無關代碼
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; // 該key原來的節(jié)點對象
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0) //第一個if,判斷是否需要擴容
n = (tab = resize()).length;
if ((first = tab[i = (n - 1) & hash]) != null) {
// 第二個if要尔,取出old Node對象
··· // 繼續(xù)省略
}
if (old != null) {// 第三個if舍杜,如果 old Node 存在
V v;
if (old.value != null)
// 如果old存在,執(zhí)行l(wèi)ambda赵辕,算出新的val并寫入old Node后返回既绩。
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) {
//如果old不存在且傳入的newVal不為null,則put新的kv
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);
}
...// 省略
}
return value;
}
解釋見注釋还惠。
compute()
String k = "key";
HashMap<String, Integer> map = new HashMap<String, Integer>() {{
put(k, 1);
}};
map.compute(k, (key, oldVal) -> oldVal + 1);
等價于
map.put(k, func(k, map.get(k)));
public Integer func(String k, Integer oldVal) {
return oldVal + 1;
}
根據已知的 k v 算出新的v并put饲握。
注意:如果無此key,那么oldVal為null吸重,lambda中涉及到oldVal的計算會報空指針互拾。
源碼和merge大同小異歪今,就不放了嚎幸。
computeIfAbsent()
由上可知,compute()有空指針的風險寄猩。所以用computeIfAbsent()
來規(guī)避嫉晶。
map.computeIfAbsent(k, key -> 1);
// 該方法等價于
map.putIfAbsent(k, 1);
// 所以computeIfAbsent在涉及到用key來計算val時才有使用價值。否則可以用putIfAbsent代替田篇。
當key不存在時替废,才compute。其他行為見注釋泊柬。
源碼略椎镣。
computeIfPresent()
compute()
的補充,key存在時才compute()
兽赁,避免潛在的空指針情況擂达。其他和compute()
相同。
源碼略冈绊。