/**
* 如果指定的值key还没有关联到一个value,那么尝试使用给定的函数计算它的值,
* 如果值不为null,则将它放入此map中
* If the specified key is not already associated with a value,
* attempts to compute its value using the given mapping function
* and enters it into this map unless {@code null}. The entire
* 整个方法调用是原子执行的,因此这个函数每个key最多使用一次。
* method invocation is performed atomically, so the function is
* applied at most once per key. Some attempted update operations
* 在计算过程中可能会阻止其他线程对该map进行的某些尝试更新操作,因此计算应该简短并且简单,
* 不得尝试更新此map的任何其他映射。
* on this map by other threads may be blocked while computation
* is in progress, so the computation should be short and simple,
* and must not attempt to update any other mappings of this map.
*
* @param key key with which the specified value is to be associated
* @param mappingFunction the function to compute a value
* @return the current (existing or computed) value associated with
* the specified key, or null if the computed value is null
* @throws NullPointerException if the specified key or mappingFunction
* is null
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the mappingFunction does so,
* in which case the mapping is left unestablished
*/
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
// 判空
if (key == null || mappingFunction == null)
throw new NullPointerException();
// 计算key 的哈希值
int h = spread(key.hashCode());
V val = null;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
// 如果table还没初始化,那么初始化table
tab = initTable();
// 如果key 对应的索引位置上还没有元素
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
// todo : 为什么不使用CAS把值放入
// -->不直接使用CAS把目标node放入的原因是:使用给定函数计算目标值可能是一个比较耗时的过程,一旦发现该
// 索引位置上没有元素,那么先创建一个ReservationNode,并使用它作为该索引位置的锁,然后获取锁,再使
// 用CAS设置进去,占住该索引位置,然后再利用给定的函数,计算其值,计算完成了再创建目标node,然后替换
// 为目标node。否则CAS失败了还得判断是否存在这个元素,然后继续处理
Node<K,V> r = new ReservationNode<K,V>();
// 先获取锁,再使用CAS设置值,避免CAS成功后,被其他线程先获取到了锁
synchronized (r) {
if (casTabAt(tab, i, null, r)) {
// CAS成功后,binCount值设置为1
binCount = 1;
Node<K,V> node = null;
try {
// 使用给定的函数计算值,参数传入key
if ((val = mappingFunction.apply(key)) != null)
node = new Node<K,V>(h, key, val, null);
// 如果计算的值为null,那么什么都不做
} finally {
// 1、如果异常了,那么会把该索引位置上的值设置回null
// 2、如果没有异常,那么会把该索引位置上的值设置为目标node
// ReservationNode 会被覆盖掉
setTabAt(tab, i, node);
}
}
}
// binCount != 0,说明数据插入完成了,也可能值为null,没有进行插入,
// 如果是计算出现异常,那么线程不会执行到这里,上面就抛出异常了
if (binCount != 0)
break;
}
// 如果当前正在进行扩容,那么先帮忙搬运数据
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
boolean added = false;
// 先获取锁
synchronized (f) {
// 获取到锁后还需要判断,该索引位置的第一个元素是否还是f
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek; V ev;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// key 已存在,获取key 映射的值
val = e.val;
break; // 结束循环
}
Node<K,V> pred = e;
// e = e.next, e.next = null说明该索引位置上没有其他元素了
if ((e = e.next) == null) {
// 使用给定的函数计算值,参数传入key
if ((val = mappingFunction.apply(key)) != null) {
added = true;
// 插入数据
pred.next = new Node<K,V>(h, key, val, null);
}
// 如果计算的值为null,那么什么都不做
// break 退出循环,binCount不会再自增
break;
}
}
}
else if (f instanceof TreeBin) {
// binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
// 进行扩容
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
// findTreeNode()查找key对应的节点,没有返回null
// 如果 p != null,说明key 已经存在
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null)
// 获取key 映射的值
val = p.val;
// 如果key不存在,使用给定的函数计算值,参数传入key
else if ((val = mappingFunction.apply(key)) != null) {
added = true;
t.putTreeVal(h, key, val);
}
}
}
}
// 如果该索引位置在插入之前已有元素,那么binCount 一定不为0,但是 tabAt(tab, i) == f值为false的
// 时候binCount的值为0,而added的值是false,所以这里必须要判断 binCount != 0
if (binCount != 0) {
// TREEIFY_THRESHOLD = 8。 binCount不包含新插入的元素,因此加上新插入的元素,slot中元素个数达到
// 9个才会转成红黑树。HashMap的computeIfAbsent()元素个数达到8个就转成红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
// added = false 说明key在之前已经存在,直接返回原来的值,元素个数不需要 +1
if (!added)
return val;
break;
}
}
}
if (val != null)
// 元素个数 + 1
addCount(1L, binCount);
// 返回原来的值/新的值
return val;
}
/**
* If the value for the specified key is present, attempts to
* compute a new mapping given the key and its current mapped
* value. The entire method invocation is performed atomically.
* Some attempted update operations on this map by other threads
* may be blocked while computation is in progress, so the
* computation should be short and simple, and must not attempt to
* update any other mappings of this map.
*
* @param key key with which a value may be associated
* @param remappingFunction the function to compute a value
* @return the new value associated with the specified key, or null if none
* @throws NullPointerException if the specified key or remappingFunction
* is null
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the remappingFunction does so,
* in which case the mapping is unchanged
*/
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
// 判空
if (key == null || remappingFunction == null)
throw new NullPointerException();
// 计算key 的hash值
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
// 如果table还没初始化,那么初始化table
tab = initTable();
// 如果该索引位置上还没有元素,那么结束循环
else if ((f = tabAt(tab, i = (n - 1) & h)) == null)
break;
// 如果当前正在进行扩容,那么先帮忙搬运数据
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// 找到key 所在的节点,使用给定的函数计算值,参数传入 key,原来的值
val = remappingFunction.apply(key, e.val);
if (val != null)
// 如果值不为null,替换原来的值
e.val = val;
else {
// 如果计算出新的值为null,那么删除这个节点
delta = -1;
// Node 只有next,没有prev
Node<K,V> en = e.next;
if (pred != null)
// pred.next 指向删除节点的下一个节点
pred.next = en;
else
// 如果删除的节点是该索引位置上的第一个节点,那么修改
// 第一个节点为: e.next
setTabAt(tab, i, en);
}
break;
}
pred = e;
// 该索引位置上没有下一个元素了,break
if ((e = e.next) == null)
break;
}
}
else if (f instanceof TreeBin) {
// binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
// 进行扩容。 在这里的主要作用就是下面的判断: if (binCount != 0) break;
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null) {
// p != null 说明 key已存在此map中
// 使用给定的函数计算新的值,参数传入 key,原来的值
// 这里把原来的值传进去了,因此可以很容易地实现在原来的值上做加减
val = remappingFunction.apply(key, p.val);
if (val != null)
// 如果新的值不为null,那么替换原来的值
p.val = val;
else {
// 如果新的值为null,那么删除此节点
delta = -1;
// removeTreeNode()如果删除后红黑树的结构太小,如果返回true,表示需要把红黑树转成链表
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (binCount != 0)
break;
}
}
// delta = -1,说明新的值为null,那么会把key所在的节点删除,因此元素个数 -1
if (delta != 0)
addCount((long)delta, binCount);
// 返回新的值
return val;
}
/**
* Attempts to compute a mapping for the specified key and its
* current mapped value (or {@code null} if there is no current
* mapping). The entire method invocation is performed atomically.
* Some attempted update operations on this map by other threads
* may be blocked while computation is in progress, so the
* computation should be short and simple, and must not attempt to
* update any other mappings of this Map.
*
* @param key key with which the specified value is to be associated
* @param remappingFunction the function to compute a value
* @return the new value associated with the specified key, or null if none
* @throws NullPointerException if the specified key or remappingFunction
* is null
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map that would
* otherwise never complete
* @throws RuntimeException or Error if the remappingFunction does so,
* in which case the mapping is unchanged
*/
// 如果key 存在,新的值不为null,那么更新key映射的值为新的值,如果新的值为null,那么删除此节点;
// 如果key 不存在,新的值不为null,那么进行插入,如果新的值为null,那么什么都不做
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
// 判空
if (key == null || remappingFunction == null)
throw new NullPointerException();
// 计算key 的hash值
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
// 如果table还没初始化,那么初始化table
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
// 如果该索引位置上还没有元素,那么创建一个ReservationNode,把该索引位置占住
Node<K,V> r = new ReservationNode<K,V>();
// 先获取锁,再使用CAS设置值,避免CAS成功后,被其他线程先获取到了锁
synchronized (r) {
if (casTabAt(tab, i, null, r)) {
binCount = 1;
Node<K,V> node = null;
try {
// 使用给定的函数计算
if ((val = remappingFunction.apply(key, null)) != null) {
// 如果计算的值不为null,那么插入到map中
delta = 1;
node = new Node<K,V>(h, key, val, null);
}
// 如果计算的值为null,那么什么都不做
} finally {
// 1、如果异常了或者新的值为null,那么会把该索引位置上的值设置回null
// 2、如果没有异常,那么会把该索引位置上的值设置为目标node
// ReservationNode 会被覆盖掉
setTabAt(tab, i, node);
}
}
}
// binCount != 0,说明数据插入完成了,也可能值为null,没有进行插入,
// 如果是计算出现异常,那么线程不会执行到这里,上面就抛出异常了
if (binCount != 0)
break;
}
// 如果当前正在进行扩容,那么先帮忙搬运数据
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
// 找到key 所在的节点,使用给定的函数计算值,参数传入 key,原来的值
val = remappingFunction.apply(key, e.val);
if (val != null)
// 如果值不为null,替换原来的值
e.val = val;
else {
// 如果计算出新的值为null,那么删除这个节点
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
// pred.next 指向删除节点的下一个节点
pred.next = en;
else
// 如果删除的节点是该索引位置上的第一个节点,那么修改
// 第一个节点为: e.next
setTabAt(tab, i, en);
}
break;
}
pred = e;
// 该索引位置上没有其他元素了,说明key在此map中不存在,进行插入处理
if ((e = e.next) == null) {
// 使用给定的函数计算值,参数传入key,null
val = remappingFunction.apply(key, null);
if (val != null) {
delta = 1;
// 插入数据
pred.next =
new Node<K,V>(h, key, val, null);
}
// 如果计算的值为null,那么什么都不做
break;
}
}
}
else if (f instanceof TreeBin) {
// binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
// 进行扩容
binCount = 1;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null)
// findTreeNode()查找key对应的节点,没有返回null
p = r.findTreeNode(h, key, null);
else
// root = null,p 没有初始化,所以初始化为null (不会存在root = null的情况)
p = null;
V pv = (p == null) ? null : p.val;
// 使用给定的函数计算值,参数传入key, pv
val = remappingFunction.apply(key, pv);
if (val != null) {
// p != null,说明key已存在
if (p != null)
// 使用新的值替换原来的值
p.val = val;
else {
// 插入数据
delta = 1;
t.putTreeVal(h, key, val);
}
}
// 新的值 val = null
else if (p != null) {
// p != null,而新的值为null,所以删除此节点
delta = -1;
// removeTreeNode()如果删除后红黑树的结构太小,如果返回true,表示需要把红黑树转成链表
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
if (binCount != 0) {
// TREEIFY_THRESHOLD = 8。 binCount不包含新插入的元素,因此加上新插入的元素,slot中元素个数达到
// 9个才会转成红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
break;
}
}
}
// 更新元素个数
if (delta != 0)
addCount((long)delta, binCount);
return val;
}
/**
* If the specified key is not already associated with a
* (non-null) value, associates it with the given value.
* Otherwise, replaces the value with the results of the given
* remapping function, or removes if {@code null}. The entire
* method invocation is performed atomically. Some attempted
* update operations on this map by other threads may be blocked
* while computation is in progress, so the computation should be
* short and simple, and must not attempt to update any other
* mappings of this Map.
*
* @param key key with which the specified value is to be associated
* @param value the value to use if absent
* @param remappingFunction the function to recompute a value if present
* @return the new value associated with the specified key, or null if none
* @throws NullPointerException if the specified key or the
* remappingFunction is null
* @throws RuntimeException or Error if the remappingFunction does so,
* in which case the mapping is unchanged
*/
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
// key 和 value 都不能为null,否则抛出NullPointerException
if (key == null || value == null || remappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {
delta = 1;
val = value;
break;
}
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = remappingFunction.apply(e.val, value);
if (val != null)
e.val = val;
else {
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
pred.next = en;
else
setTabAt(tab, i, en);
}
break;
}
pred = e;
if ((e = e.next) == null) {
delta = 1;
val = value;
pred.next =
new Node<K,V>(h, key, val, null);
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r