0
点赞
收藏
分享

微信扫一扫

HashMap的remove相关方法


HashMap的remove相关方法

前面增加和查询都解析完了,这里我们看一下跟删除相关的方法。

Remove key

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
  • 存在;则同样需要分为三种情况处理
  • 红黑树结构,则执行removeTreeNode方法进行删除【不深究】
  • 数组上的位置就是目标节点。即node与p相等,则直接将node的next放置在数组第i个位置上。
  • 如果不是在数组的第i位置,则需要通过段链的方式来删除节点,即此时节点p是node的上一个节点,只需要把p的next执行node的next即可。

我们再回过头来看这个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))))

Remove key value

我们再看一下传入两个值的方法

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方法就解析这么多了


举报

相关推荐

0 条评论