1. remove
- remove 有两个重载函数
- V remove(Object key):移除指定 key 的元素,删除成功返回 value 值,没找到返回 null
- boolean remove(Object key, Object value):移除指定 key 的元素,但对应key节点的value必须与 传入的value值相等,才会进行删除,返回值为 boolean
/* 可以看到,两个函数的区别就是 removeNode() 的第四个参数,与返回值类型不同 */
public V remove(Object key) {
Node<K,V> e;
/* removeNode: 移除指定 key 的 Node 节点 */
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
@Override
publicboolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
- removeNode 函数源码
/* matchValue:为true表示只有key,value值都相同是才删除,由于调用key删除,默认为false */
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
/* tab:存储数据的数组, p: hash对应桶位的首元素, n:tab 数组的长度, index: hash对应的桶位下标 */
Node<K,V>[] tab; Node<K,V> p; int n, index;
/* tab不为空 && tab长度大于 0 && hash 值对应桶位首元素不为 null */
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
/* node: 存储key对应的节点,默认为null,e: 用于循环的中继节点 */
Node<K,V> node = null, e; K k; V v;
/* 判断 p.key 是否与传入的 key 相等 */
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
/* 若 p.next == null 则该桶只有一个元素 */
else if ((e = p.next) != null) {
/* 判断 p 是否为树节点 */
if (p instanceof TreeNode)
/* 树节点查找节点,可回看 02 篇 */
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
/* 链节点查找 */
do {
/* 判断key值是否相等 */
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break; // 找到则退出当前循环
}
p = e;
} while ((e = e.next) != null);
}
}
/* node不为null && (!matchValue 默认为true,为false 继续判断value是否相等 ) */
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
/* 若 node 为树节点, 调用 TreeNode 的移除节点,回看 02 篇 */
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
/* 若 node不为树节点且为 头结点, 将桶位首元素指向 next 即可 */
else if (node == p)
tab[index] = node.next;
/* 链表删除,由于是单向链表,p.next 指向 node.next 即可 */
else
p.next = node.next;
/* 更新 modCount,size */
++modCount;
--size;
/* 预留函数 */
afterNodeRemoval(node);
/* 返回删除的节点 */
return node;
}
}
/* 找不到返回 null */
return null;
}
2. clear()
- 清空所有元素
/* 循环tab的每个桶位,将首元素置为null */
public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
3. putIfAbsent
- 添加新元素,若key已存在,不替换,除非值为 null,详解 03 篇,这里不做赘述
@Override
publicV putIfAbsent(K key, V value) {
/* param4: onlyIfAbsent 为true 即不替换 */
return putVal(hash(key), key, value, true, true);
}
/* putVal找到该元素的源码
if (!onlyIfAbsent || oldValue == null)
e.value = value;
*/
4. 替换函数 replace()
- replace有两个重载函数
- replace(K key, V oldValue, V newValue):找到key对应的节点后,还需比对 oldValue 才能进行替换,返回值为 boolean
- replace(K key, V value):找到key对应的节点之后,直接替换值,并返回旧值,简单来说,就是不要验证身份,返回值为 oldValue 。
@Override
public boolean replace(K key, V oldValue, V newValue) {
Node<K,V> e; V v;
/* 找到 key 对应的节点 && value 与 oldValue 相等,才置换为 新值 */
if ((e = getNode(hash(key), key)) != null &&
((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
e.value = newValue;
/* 预留函数 */
afterNodeAccess(e);
return true;
}
/* 没找到返回 false */
return false;
}
@Override
public V replace(K key, V value) {
Node<K,V> e;
/* 找到节点,直接替换为新值,返回旧值 */
if ((e = getNode(hash(key), key)) != null) {
V oldValue = e.value;
e.value = value;
/* 预留函数 */
afterNodeAccess(e);
return oldValue;
}
return null;
}
5. 批量替换函数 replaceAll()
- 给定执行动作function,替换所有节点的值
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Node<K,V>[] tab;
/* 执行动作 null 判断 */
if (function == null)
throw new NullPointerException();
/* 元素个数 > 0 && tab 不为空 */
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);
}
}
/* 修改当前版本号,但是目前value值已经替换完毕 */
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
6. 总结
- 注意一点,replaceAll 报错ConcurrentModificationException,但此时值已经改变完。
import java.util.HashMap;
public class HashMapTest02 {
public static void main(String[] args) {
HashMap<String, Integer> hashMap = new HashMap<>();
for(int i = 1; i <= 10; i++) {
hashMap.put("我是" + i, i);
}
System.out.println("now hashMap: " + hashMap);
System.out.println("hashMap.remove('我是1'): " + hashMap.remove("我是1"));
System.out.println("hashMap.remove('我是2', 2): " + hashMap.remove("我是2",2));
System.out.println("hashMap.remove('我是3', 30): " + hashMap.remove("我是3",30));
System.out.println("now hashMap: " + hashMap);
System.out.println("==============================");
System.out.println("hashMap.putIfAbsent('我是11', 11): " + hashMap.putIfAbsent("我是11", 11));
System.out.println("hashMap.putIfAbsent('我是10', 100): " + hashMap.putIfAbsent("我是10", 100));
System.out.println("now hashMap: " + hashMap);
System.out.println("==============================");
System.out.println("hashMap.replace('我是5', 50): " + hashMap.replace("我是5", 50));
System.out.println("hashMap.replace('我是6,', 6, 60): " + hashMap.replace("我是6", 6, 60));
System.out.println("hashMap.replace('我是7,', 17, 70): " + hashMap.replace("我是7", 17, 70));
System.out.println("now hashMap: " + hashMap);
System.out.println("==============================");
new Thread(() -> {
try {
Thread.sleep(100);
hashMap.put("我是13", 13);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
try {
hashMap.replaceAll((k, v) -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
v = v * 10;
return v;
});
}catch (Exception e) {
e.printStackTrace();
}
System.out.println("now hashmap: " + hashMap);
hashMap.clear();
System.out.println("now hashmap: " + hashMap);
}
}