前面增加和查询都解析完了,这里我们看一下跟删除相关的方法。
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
}
通过源码可以看到,主要的功能方法还是里面的removeNode方法,我们看一下removeNode方法:
final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {
//移除的条件:
// 1.HashMap中table属性不能为null,
// 2.table的程度需要大于0,
// 3.通过hash计算出来的下标元素需要存在
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
//(hash值相等) 并且 (key地址等或key equals为true)
//如果条件都成立,表示table[(n-1)&hash]上的元素就是需要找的元素
node = p;
else if ((e = p.next) != null) {
//否则,先取出table[(n-1)&hash]的next,判断是否为null
if (p instanceof TreeNode)
//如果此时节点是红黑树类型,则通过树的方式来查询
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
//链表方式
do {
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
//同样获取到hash相同,并且key地址相同或equals为true,则退出
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
//此时如果node为null,则表示没有查到。
//如果不为空,当matchValue为true时,才需要后面的值相等时会被删除,否则不用比较值,这个节点就会被删除
if (node instanceof TreeNode)
//红黑树
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
//如果与table[(n-1)&hash]相等
tab[index] = node.next;
else
//链表
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
这里是remove方法的源码,虽然注释也写了很多,但是我们还是一步一步来分析一遍。
if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {}
首先还是前提条件要保证:① 数组不为null ② 数组的长度大于0 ③ 计算到的hash值对应的数组位置节点不为null。【index计算请看put方法相应文章】
继续:
其实我们会发现,查询和删除都有相同的一步,就是需要先把这个key指定的节点找出来。再去执行后面的操作。所以我们可以看到,removeNode前面一部分代码和查询的代码是相似的。
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
//(hash值相等) 并且 (key地址等或key equals为true)
//如果条件都成立,表示table[(n-1)&hash]上的元素就是需要找的元素
node = p;
else if ((e = p.next) != null) {
//否则,先取出table[(n-1)&hash]的next,判断是否为null
if (p instanceof TreeNode)
//如果此时节点是红黑树类型,则通过树的方式来查询
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
//链表方式
do {
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
//同样获取到hash相同,并且key地址相同或equals为true,则退出
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
同样情景也是分为三种:
具体细节就不再赘述,跟前面get方法一直。
继续:
if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) {
//此时如果node为null,则表示没有查到。
//如果不为空,当matchValue为true时,才需要后面的值相等时会被删除,否则不用比较值,这个节点就会被删除
if (node instanceof TreeNode)
//红黑树
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
//如果与table[(n-1)&hash]相等
tab[index] = node.next;
else
//链表
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
执行完查询之后就是需要执行具体的删除操作了。
首先需要判断指定的节点是否存在
不存在直接返回null
存在;则同样需要分为三种情况处理
我们再回过头来看这个remove方法的声明:
public V remove(Object key)
只传入一个key,来删除,即不用通过比较value的值,遇到相同的key就删除,所以在调用removeNode的时候,matchValue参数传入的是false,即这个值为false,!matchValue就为true,则后面的值value就不用比较了。
if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v))))
我们再看一下传入两个值的方法
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
通过前面的分析,我们大概可以猜到,这个方法移除条件是建立在key和value都相同的时候,才移除节点。所以matchValue传入的是true。此时的!matchValue就是false,所以需要看后面value判断是否为true。
if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v))))
至此,HashMap的remove方法就解析这么多了