IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【Java集合】HashMap的remove()源码详解以及JDK1.7与JDK1.8的区别 -> 正文阅读

[数据结构与算法]【Java集合】HashMap的remove()源码详解以及JDK1.7与JDK1.8的区别

目录

一、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.7remove()方法源码

流程与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的区别

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:55:08  更:2022-02-26 11:56:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 16:29:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码