目录
一、remove()方法的执行流程
二、remove()方法源码
三、JDK1.7的remove()方法源码
一、remove()方法的执行流程
流程图:
二、remove()方法源码
HashMap中有两个remove()方法,一般常用的是第一个
1、以key为参数的remove方法 输入key–>key存在就删除,若删除成功则返回被删除的元素的value,删除失败返回null
public V remove(Object key) {
// 被删除的元素
Node<K,V> e;
// 返回移除的节点的value值
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
2、以key+value为参数的remove方法 必须key和value都相同才删除
public boolean remove(Object key, Object value) {
// 返回是否成功移除节点
return removeNode(hash(key), key, value, true, true) != null;
}
HashMap.remove(key) 不去判断值相不相等。
HashMap.EntrySet.remove(key)、HashMap.remove(key,value)、LinkedHashMap.LinkedEntrySet.remove(key)需要判断value相不相等
但是两个方法都是在调用removeNode()方法
/**
移除某个节点,根据下面四个条件进行移除
hash - key 的hash值
key - key
matchValue - 如果为true,则仅在值相等时删除;如果是false,则值不管相不相等,只要key和hash值一致就移除该节点。
movable - 如果为false,则在删除时不移动其他节点
return - 返回被移除节点,未找到则返回null
*/
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
// tab:当前map 的数组,p:hash对应的数组索引index位置上的节点/遍历链表时表示当前遍历到的节点的前一个节点,n:数组长度,index:hash对应的数组索引
// 这几个值在hashMap的源码中很常见
Node<K,V>[] tab; Node<K,V> p; int n, index;
// 前提判断 数组不为空,并且长度大于0 并且
// hash对应的数组索引位置上的节点p也不为null
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
// node:被移除的节点,e:当前头节点的下一个节点/遍历链表时表示当前遍历到的节点,
// k:e节点的key,v:被移除节点node 的value
Node<K,V> node = null, e; K k; V v;
// 如果第一个节点p就是目标节点,则将node指向第一个节点p
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k)))){
node = p;
}
// 第一个节点不是,那就看看第一个节点还有没有下一个元素。
// 如果有第二个节点
else if ((e = p.next) != null) {
// 如果刚刚第一个节点是红黑树
if (p instanceof TreeNode){
// 调用红黑树的查询节点的方法,getTreeNode()
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
}
// 第一个节点不是红黑树,并且还有第二个节点,那就说明,这里是链表了
else {
// 那么开始循环链表,从第二个节点开始循环,因为第一个节点已经处理过了
do {
// 判断e节点是不是目标节点,是的话就将node指向e,并且终止循环
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
// e节点不是目标节点,那就将p节点指向e节点,
// 然后while里面e节点后移,在进入循环后发现e是目标节点了,退出循环,退出后此时p节点还是e节点的前一个节点,也就保证了在整个循环的过程中,p节点始终是e节点的前一个节点。
p = e;
} while ((e = e.next) != null);// e指针后移,并且下一个节点不为null则继续遍历,不为null表示没到链表最后。
}
}
// 找到目标节点了 matchValue为true,则仅在值相等时删除。如果是false,则值不管相不相等,只要key和hash值一致就移除该节点。
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
// 如果目标节点是红黑树
if (node instanceof TreeNode){
// 调用红黑树的删除节点方法
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
}
// 目标节点是p节点,
// 还记得之前 如果第一个节点p(数组桶中的节点)就是目标节点,则将node指向第一个节点p
else if (node == p){
// 将目标节点的下一个节点作为该索引位置的第一个元素
// 也就是跳过目标节点,指向目标节点的下一位
tab[index] = node.next;
}
// 这里就是遍历链表找到了目标节点
else{
// p节点始终作为node的上一个节点,p.next始终指向目标节点node
// 现在将p.next 指向目标节点node的next,这样跳过了目标节点node,就把node移除掉了
p.next = node.next;
}
// 记录map结构被修改的次数,主要用于并发编程
++modCount;
// 记录table存储了多少键值对,因为移除了一个,所以此处就减一
--size;
// 该方法在hashMap中是空方法,主要是供LinkedHashMap使用,因为LinkedHashMap重写了该方法
afterNodeRemoval(node);
// 返回被移除的节点
return node;
}
}
// 没找到 返回null
return null;
}
三、JDK1.7的remove()方法源码
流程与JDK1.8基本一致,只是没有红黑树结构
/**
* 函数:remove(Object key)
* 作用:删除该键值对
* @return 返回被删除的value值,如果删除失败返回Null
*/
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
// 返回移除的节点的value值
return (e == null ? null : e.value);
}
final Entry<K,V> removeEntryForKey(Object key) {
// HashMap为空,直接返回Null
if (size == 0) {
return null;
}
// 1. 计算hash值
int hash = (key == null) ? 0 : hash(key);
// 2. 计算存储的数组下标位置
int i = indexFor(hash, table.length);
// 定位到key值对应的数组位置
// prev在后面遍历链表时指向当前节点的前一个节点,e表示当前遍历到的节点
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
// 遍历链表,找到key和hash值与传入参入相同的节点,即为要删除的节点
while (e != null) {
// 记录当前节点的下一个节点
Entry<K,V> next = e.next;
Object k;
// 如果key值和hash值相同,则找到目标节点。执行移除操作
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
// 记录map结构被修改的次数,主要用于并发编程
modCount++;
// 记录table存储了多少键值对,因为移除了一个,所以此处就减一
size--;
// 若删除的是table数组中的元素(即链表的头结点)
// 则删除操作 = 将头结点的next引用存入table[i]中
if (prev == e)
table[i] = next;
//否则 将以table[i]为头结点的链表中,当前Entry的前1个Entry中的next 设置为 当前Entry的next(即删除当前Entry = 直接跳过当前Entry)
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
// 返回删除的节点
return e;
}
?相关文章:【Java集合】HashMap的put()源码详解以及JDK1.7与JDK1.8的区别
? ? ? ? ? ? ? ? ??【Java集合】HashMap的resize()源码详解以及JDK1.7与JDK1.8的区别
? ? ? ? ? ? ? ? ??【Java集合】HashMap的get()源码详解以及JDK1.7与JDK1.8的区别