HashMap 的底层原理和源码分析

news2024/11/28 9:26:48

tip:作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。

推荐:体系化学习Java(Java面试专题)

文章目录

  • 一、HashMap 的底层原理
  • 二、put方法源码分析
  • 三、get 方法源码分析
  • 四、remove 方法源码分析

一、HashMap 的底层原理

HashMap 是 Java 中常用的一种数据结构,它是基于哈希表实现的。下面是 HashMap 的底层原理:

HashMap 内部维护了一个数组,称为哈希表,每个元素都是一个链表的头结点,该链表被称为桶(bucket)。当我们向 HashMap 中添加一个元素时,首先会根据该元素的键值(key)计算出一个哈希值(hash code),然后根据哈希值确定该元素在数组中的位置,如果该位置上已经存在了元素,则将该元素添加到该位置上对应的桶中,如果该位置上没有元素,则创建一个新的桶,并将该元素添加到该桶中。

在这里插入图片描述

HashMap 的哈希值是通过调用键值的 hashCode 方法计算得到的。由于哈希值可能会发生冲突,因此每个桶中可能会有多个元素。当我们需要查找元素时,首先根据键值的哈希值确定该元素所在的桶,然后遍历该桶中的所有元素,找到与给定键值相等的元素并返回。

为了提高 HashMap 的查找效率,Java 8 中引入了红黑树(Red-Black Tree)的概念。当一个桶中的元素个数达到一定阈值(默认为 8)时,该桶中的元素将被转化为一棵红黑树,从而提高查找效率。当然,如果桶中的元素个数小于等于阈值,则仍然采用链表的方式进行存储。

HashMap 的扩容机制是在数组大小达到一定阈值(默认为 75%)时进行的。当数组大小达到阈值时,HashMap 会创建一个新的数组,将原数组中的元素重新哈希到新数组中,并将新数组作为 HashMap 的哈希表。这个过程需要重新计算每个元素在新数组中的位置,因此比较耗时。

HashMap 是非线程安全的,如果多个线程同时对 HashMap 进行修改,可能会导致数据不一致的情况。如果需要在多线程环境下使用 HashMap,可以使用 ConcurrentHashMap。

二、put方法源码分析

public V put(K key, V value) {
   
    // 计算 key 的哈希值
    int hash = hash(key);
    
    // 根据哈希值和数组长度计算出 key 在数组中的位置
    int i = indexFor(hash, table.length);
    
    // 遍历该位置上的链表,查找是否已经存在该 key
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        // 如果该 key 已经存在,则更新其对应的 value,并返回旧的 value
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    
    // 如果该 key 不存在,则将其添加到链表的头部
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

put 方法的主要流程如下:

  1. 计算 key 的哈希值。
  2. 根据哈希值和数组长度计算出 key 在数组中的位置。
  3. 遍历该位置上的链表,查找是否已经存在该 key,如果存在,则更新其对应的 value,并返回旧的 value。
  4. 如果该 key 不存在,则将其添加到链表的头部。

在添加元素时,HashMap 会将元素添加到链表的头部,这是因为在遍历链表时,我们通常会从头部开始遍历,这样可以提高查找效率。如果链表中已经存在该 key,则将其对应的 value 更新为新的值。如果链表中不存在该 key,则将其添加到链表的头部,并将 modCount 属性加 1,表示 HashMap 的结构已经发生了变化。

三、get 方法源码分析

public V get(Object key) {
    
    // 如果 key 为 null,则返回 null
    if (key == null)
        return getForNullKey();
    
    // 计算 key 的哈希值
    int hash = hash(key);
    
    // 根据哈希值和数组长度计算出 key 在数组中的位置
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        // 如果找到了对应的 key,则返回其对应的 value
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
            return e.value;
    }
    
    // 如果没有找到对应的 key,则返回 null
    return null;
}

get 方法的主要流程如下:

  1. 如果 key 为 null,则返回 null。
  2. 计算 key 的哈希值。
  3. 根据哈希值和数组长度计算出 key 在数组中的位置。
  4. 遍历该位置上的链表,查找是否存在对应的 key,如果存在,则返回其对应的 value。
  5. 如果链表中不存在对应的 key,则返回 null。

在查找元素时,HashMap 会根据 key 的哈希值确定其在数组中的位置,然后遍历该位置上的链表,查找是否存在对应的 key。如果存在,则返回其对应的 value;如果不存在,则返回 null。由于哈希值可能会发生冲突,因此同一个桶上可能会有多个元素,需要遍历链表才能找到对应的元素。

四、remove 方法源码分析

public V remove(Object key) {
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
}

final Entry<K,V> removeEntryForKey(Object key) {
   // 如果 key 为 null,则返回 null
   if (key == null)
       return null;
  
   // 计算 key 的哈希值
   int hash = hash(key);
   
   // 根据哈希值和数组长度计算出 key 在数组中的位置
   int i = indexFor(hash, table.length);
  
   // 遍历该位置上的链表,查找是否存在对应的 key
   Entry<K,V> prev = table[i];
   Entry<K,V> e = prev;
   while (e != null) {
       Entry<K,V> next = e.next;
       Object k;
       // 如果找到了对应的 key,则将其从链表中删除
       if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
           modCount++;
           size--;
           if (prev == e)
               table[i] = next;
           else
               prev.next = next;
           e.recordRemoval(this);
           return e;
       }
       prev = e;
       e = next;
   }
   
   // 如果没有找到对应的 key,则返回 null
   return null;
}

remove 方法的主要流程如下:

  1. 调用 removeEntryForKey 方法查找对应的 Entry 对象。
  2. 如果找到了对应的 Entry 对象,则将其从链表中删除,并返回其对应的 value。
  3. 如果没有找到对应的 Entry 对象,则返回 null。

在删除元素时,HashMap 会先调用 removeEntryForKey 方法查找对应的 Entry 对象,然后将其从链表中删除。如果找到了对应的 Entry 对象,则将其从链表中删除,并返回其对应的 value;如果没有找到对应的 Entry 对象,则返回 null。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/620485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【迷宫】地下迷宫游戏-微信小程序开发流程详解

可曾记得&#xff0c;小时候上学路边买的透明铅笔盒&#xff0c;里面内嵌了一个小球&#xff0c;它用重力可从起点滚动到终点&#xff0c;对小朋友来说是感觉有趣的&#xff0c;在这个游戏的基础上&#xff0c;弄一款微信小程序的迷宫探索游戏试试&#xff0c;在不同关卡的迷宫…

14 【Vuex】

1.理解 Vuex 1.1 Vuex 是什么 概念&#xff1a;专门在Vue中实现集中式状态&#xff08;数据&#xff09;管理的一个Vue插件&#xff0c;对Vue应用中多个组件的共享状态进行集中式的管理&#xff08;读/写&#xff09;&#xff0c;也是一种组件间通信的方式&#xff0c;且适用…

基于RK3399+FPGA的地面测试台多参数数据记录仪方案(一)软件设计及测试

完成了测试台软件分析和编程环境搭建后&#xff0c;接下来就是软件的编写。本章主要包括 软件窗口界面设计和功能代码实现。以某型号数据记录仪的工作需求为目标&#xff0c;根据测试 工作流程&#xff0c;以 Linux-Qt 为主要开发手段&#xff0c;设计一款功能完备、界面友…

Java8 Stream详解及结束操作方法使用示例(三)

结束操作是指结束 Stream 该如何处理的操作&#xff0c;并且会触发 Stream 的执行。下面是一些常用的结束操作方法。结束操作会对数据源进行遍历&#xff0c;因此是及早求值的。 Java8 Stream详解及中间操作方法使用示例&#xff08;一&#xff09; ​​​​​​​Java8 Strea…

三种经典博弈(取石子问题)

三种经典博弈 巴什博奕威佐夫博奕尼姆博奕 博弈是有一种很有意思的游戏&#xff0c;就是有物体若干堆&#xff0c;可以是火柴棍或是围棋子等等均可。两个人轮流从堆中取物体若干&#xff0c;规定最后取光物体者取胜。这是我国民间很古老的一个游戏&#xff0c;别看这游戏极其简…

一体化协同平台助力企业回归生产本质,创造价值

核心观点 单点工具的串联无法有效解决研效痛点问题&#xff0c;企业需要通过一体化协同平台提高端到端价值流动效率。一体化协同平台的价值是软件工程理念最大化落地、数字化研发管理、沉浸式研发体验。一体化协同平台集成需要评估闭环效率杠杆&#xff0c;确定集成边界和集成…

什么是关系模型? 关系模型的基本概念

关系模型由IBM公司研究员Edgar Frank Codd于1970年发表的论文中提出&#xff0c;经过多年的发展&#xff0c;已经成为目前最常用、最重要的模型之一。 在关系模型中有一些基本的概念&#xff0c;具体如下。 (1)关系(Relation)。关系一词与数学领域有关&#xff0c;它是集合基…

TensorRT 从7.2升级到8.5,改写plugin以适配新版本

前言 TensorRT是NVIDIA推出的一款高效深度学习模型推理框架&#xff0c;其包括了深度学习推理优化器和运行时&#xff0c;能够让深度学习推理应用拥有低时延和高吞吐的优点。 TensorRT的版本迭代速度非常快&#xff0c;很多之前写的plugin在版本升级后可能就没法直接使用&…

广州虚拟动力携数字人交互技术产品参展第十九届深圳文博会

2023年6月7-11日 深圳国际会展中心 第十九届深圳文博会正式举办&#xff01; 广州虚拟动力携数字人交互技术产品参展 诚邀您莅临粤港澳大湾区馆&#xff08;11号馆&#xff09; 广东参展团展位参观交流 数字技术&#xff0c;引领文化产业新发展 中国文化及相关文化产业的…

【Apache Pinot】Controller、Broker 和 Server 的概念和工作流程

背景 笔者最近一段时间使用 Apache Pinot 比较多&#xff0c;发现目前国内使用 Pinot 的很少&#xff0c;所以跟他相关的资料也比较少&#xff0c;本人在扩容&#xff0c;升级&#xff0c;部署&#xff0c;查询等方面操作有些许经验&#xff0c;知道其中有很多细节需要注意和规…

北邮22信通:实验七 三角波-方波(锯齿波-矩形波)发生器实验报告(着急验收的同学先看看,后续细节正在赶来中)

北邮22信通一枚~ 持续更新模电实验讲解 关注作者&#xff0c;解锁更多邮苑模电实验报告~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22信通——电子电路_青山如墨雨如画的博客-CSDN博客 实验七 三角波-方波&#xff08;锯齿波-矩形波&#xff09;发生器实验…

数据结构——树的概念、二叉树的概念

文章目录 引言1.树的概念1.1.树的其他相关概念 2.树的代码实现的结构2.1.树形结构的应用 3.二叉树的概念3.1.特殊二叉树的概念3.1.1.完全二叉树3.1.2.满二叉树 3.2.二叉树试题讲解3.2.1.试题一3.2.2.试题二3.2.3.试题三 4.二叉树的存储结构4.1.顺序结构存储4.2.链式结构存储 引…

重温经典:简读光干涉、衍射原理

如果您不是光学专业的&#xff0c;或者是文科生&#xff0c;那么您想到光的干涉和衍射第一反应应该是很多公式对不对&#xff1f;头好大是不是&#xff1f;好&#xff0c;那么今天我们就不用一个公式来重新解读光的干涉和衍射。 光&#xff0c;也叫电磁波&#xff0c;他的表现…

java并发编程:Java线程池详解

文章目录 为什么要用线程池线程池的原理ThreadPoolExecutor提供的构造方法ThreadPoolExecutor的策略线程池主要的任务处理流程ThreadPoolExecutor如何做到线程复用的&#xff1f; 四种常见的线程池newCachedThreadPoolnewFixedThreadPoolnewSingleThreadExecutornewScheduledTh…

web3.js获取导入

我们访问 https://github.com/ 我们搜索 web3.js 然后我们直接点击第一个进去 进入之后 往下拉 你会看到 它支持node项目的两种引入方式 这里 大家可以直接下载我的资源 https://download.csdn.net/download/weixin_45966674/87878737 下载好解压出来就会有一个 web3.min.js…

C#读写EM4205/4305/4469卡复制ID卡制做FDX-B动物标签源码

EM4305/EM4205卡是采用瑞士EM微电子公司工作频率为125kHz&#xff0c;具有读、写功能的非接触式RFID射频芯片&#xff0c;它具有功耗低、可提供多种数据传输速率和数据编码方法等特点&#xff0c;适合射频芯片ISO 11784/11785规范&#xff0c;该芯片被广泛应用于动物识别和跟踪…

《C++高级编程》读书笔记(五、六:面向对象设计设计可重用代码)

1、参考引用 C高级编程&#xff08;第4版&#xff0c;C17标准&#xff09;马克葛瑞格尔 2、建议先看《21天学通C》 这本书入门&#xff0c;笔记链接如下 21天学通C读书笔记&#xff08;文章链接汇总&#xff09; 1. 过程化的思考方式 过程语言&#xff08;例如 C&#xff09;将…

玄奘文旅集团主办学习强企玄奘之路戈壁挑战赛在敦煌圆满完赛!

“学习改变命运&#xff0c;强企复兴中国”&#xff0c;2023年4月16日-20日&#xff0c;由玄奘文旅集团主办的“第23届26届学习强企玄奘之路戈壁挑战赛&#xff08;联赛&#xff09;”在千年古城敦煌圆满落幕&#xff01; 这是商界各行业的一次跨界融合&#xff0c;也是疫情过…

感性了解一下互斥和信号量

一、互斥的四个概念 我们把大家都能看到的资源叫做&#xff1a;公共资源 a、互斥&#xff1a;任何一个时刻&#xff0c;都只允许一个执行流在进行共享资源的访问——加锁 b、我们把任何一个时刻&#xff0c;都只允许一个执行流进行访问的共享资源叫做临界资源 c、临界资源需…

【TA100】图形 2.4 传统经验光照模型详解

一、光照模型 ● 一种模拟自然光照过程的计算机模型 ● 本次课程可以这样理解&#xff1a;光线与物体表面的作用 ● 分类 ○ 基于物理的光照模型&#xff08;PBR&#xff09;&#xff08;有可依据的公式&#xff09; ○ 经验模型&#xff08;进行了一些近似、模拟&#xff0c;…