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

你可能感兴趣的:(基础)