【 java 集合】HashMap源码分析

news2024/11/18 17:51:26

📋 个人简介

  • 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜
  • 📝 个人主页:馆主阿牛🔥
  • 🎉 支持我:点赞👍+收藏⭐️+留言📝
  • 📣 系列专栏:java 小白到高手的蜕变🍁
  • 💬格言:要成为光,因为有怕黑的人!🔥
    请添加图片描述

目录

    • 📋 个人简介
  • 前言
    • HashMap源码分析
    • 总结
  • 结语

前言

HashMap的底层原理一直以来是面试必问的问题,本节将基于源码分析一下!jdk7版本的HashMap和jdk8的HsahMap底层原理不同,本文主要以jdk8里的HashMap来分析,最后会总结jdk7与jdk8中HashMap的底层原理,用来对比不同!

HashMap源码分析

在分析源码之前,你先要了解HashMap源码中的几个重要常量:

DEFAULT _ INITIAL _ CAPACITY : HashMap 的默认容量,16
MAXIMUM _ CAPACITY : HashMap 的最大支持容量,2^30
DEFAULTLOAD _ FACTOR : HashMap 的默认加载因子,0.75
TREEIFY _ THRESHOLD : Bucket 中链表长度大于该默认值,转化为红黑树,8
UNTREEIFY _ THRESHOLD : Bucket 中红黑树存储的 Node 小于该默认值,转化为链表 。
MIN _ TREEIFY _ CAPACITY :桶中的 Node 被树化时最小的 hash 表容量。(当桶中 Node 的数量大到需要变红黑树时,若 hash 表容量小于 MIN _ TREEIFY _ CAPACITY 时,此时应执行 resize 扩容操作这个 MIN _ TREEIFY _ CAPACITY 的值至少是 TREEIFY _ THRESHOLD 的4倍。
table :存储元素的数组,总是2的 n 次幕
entrySet :存储具体元素的集
size : HashMap 中存储的键值对的数量
modCount : HashMap 扩容和结构改变的次数。
threshold :扩容的临界值,=容量*填充因子=16*0.75=12
loadFactor :填充因子,创建HashMap时会将 DEFAULTLOAD _ FACTOR 赋值给它,即0.75

标红的几个变量要尤其注意!
在这里插入图片描述
那么接下来我们来分析了!
首先创建HashMap对象:
在这里插入图片描述
首先给填充因子loadFactor赋值为0.75!
然后再来看看装这个key-value的Node类型的数组以及Node内部类(jdk7为Entry类型的数组,都一样,换了个名)
在这里插入图片描述
在这里插入图片描述

然后我们就需要从put方法入手看看发生了什么:
在这里插入图片描述
需要计算一个key的Hash值
在这里插入图片描述
用到了HashCode方法,这就是为啥我们说重写equals有时要重写HashCode,我们当时在HashSet那片中写过!

putVal这个方法里分支较多,我们要慢慢看,不难!

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

在这里插入图片描述
这一块说明数组为空,也就是我们刚开始添加数据!
此时执行resize()方法扩容!
这个resize的逻辑分支也多,通过分析,首次put元素,resize会将底层数组长度创建好,长度为16!

在这里插入图片描述
在这里插入图片描述

我们继续回到putVal方法:
在这里插入图片描述
如果当前的要添加元素的数组位置为空,则将这个元素添加到这个位置!
如果数组位置不为空
在这里插入图片描述
在这里插入图片描述

如果要添加元素Hash值和当前位置元素的Hash值相同,且key值也相同,则替换value值。
如果Hash值不同或者key值不同,则循环比较链表中的元素
在这里插入图片描述
还有一层就是当前链表长度大于等于8时,转换为红黑树存储链表元素
在这里插入图片描述
但我们进入到treeifyBin发现当前链表长度大于等于8时,不一定转换为红黑树存储链表元素
在这里插入图片描述
还要满足当前数组长度大于64才转变为红黑树,否则就扩容一下底层数组,让链表长度短一些就行,毕竟红黑树是个比较复杂的树形结构!

关于扩容:
在这里插入图片描述

总结

jdk7
HashMap map = newHashMap ();
在实例化以后,底层创建了长度是16的一数组 Entry [ ] table。
然后进行put (key1,value1)添加元素:
首先,调用key1所在类的 hashCode ()计算key1哈希值,此哈希值经过某种算法计算以后,得到在 Entry 数组中的存放位置。

  • 如果此位置上的数据为空,此时的key1-value1添加成功。——>情况一
  • 如果此位量上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据key的哈希值:
    • 如果key1的哈希值与已经存在的数据的哈希值不相同,此时key1-value1添加成功。——>情况二
    • 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的 equals(key2)
      • 如果 equals ()返回 false :此时key1-value1添加成功。——>情况三
      • 如果 equals ()返回 true :使用value1替换value2。

补充:关于情况二和情况三:此时key1-value1和原来的数据以链表的方式存储,在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:数组长度扩容为原来容量的2倍,并将原有的数据复制过来。扩容临界值也扩为原来的2倍!

jdk8
jdk8相较于jdk7在底层实现方面的不同:

  1. new HashMap():底层没有创建一个长度为16的数组。
  2. jdk8底层的数组是: Node [],而非 Entry []。
  3. 首次调用 put ()方法时,底层创建长度为16的数组。
  4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。当数组的某一个索引位置上的元素以链表形式存在的数据个数>=8且当前数组的长度>64时,此时此索引位置上的所有数据改为使用红黑树存储。

结语

如果你觉得博主写的还不错的话,可以关注一下当前专栏,博主会更完这个系列的哦!也欢迎订阅博主的其他好的专栏。

🏰系列专栏
👉软磨 css
👉硬泡 javascript
👉flask框架快速入门

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

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

相关文章

python基础篇之列表(增删改查)

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a;lqj_本人的博客_CSDN博客-微信小程序,前端,vue领域博主lqj_本人擅长微信小程序,前端,vue,等方面的知识https://blog.csdn.net/lbcyllqj?spm1000.2115.3001.5343 哔哩哔哩欢迎关注&…

excel数据统计:三个公式提高统计工作效率

善于在工作中使用函数、公式可以提高工作效率&#xff0c;结合近期学员们遇到的问题&#xff0c;老菜鸟总结了三个非常实用的公式&#xff0c;每个公式都可以解决一类问题。学会这三个公式套路&#xff0c;就能解决日常遇到的很多麻烦事。第一类问题&#xff1a;对指定时间段的…

通过nvm 控制node的常见命令

通过nvm 控制node查看本电脑安装的node版本号切换到对应的node版本号可以查看nvm的全部命令查看node可安装的全部版本号下载对应node的版本查看本电脑安装的node版本号 nvm ls 查看本电脑安装的node版本号 切换到对应的node版本号 nvm use 版本号 切换到对应的node版本号 注意…

基于Python + Django 的密码自助平台项目(完整代码)

场景说明&#xff1a;因为本公司 AD 是早期已经在用&#xff0c;用户的个人信息不是十分全面&#xff0c;例如:用户手机号。 钉钉是后来才开始使用&#xff0c;钉钉默认是使用手机号登录。 用户自行重置密码时如果通过手机号来进行钉钉与 AD 之间的验证就行不通了。逻辑&#x…

Linux驱动开发基础_在设备树中指定中断以及在代码中获得中断

目录 1 设备树里中断节点的语法 1.1 设备树里的中断控制器 1.2 设备树里使用中断 2 设备树里中断节点的示例 3 在代码中获得中断 3.1 对于 platform_device 3.2 对于 I2C 设备、SPI 设备 3.3 调用 of_irq_get 获得中断号 3.4 对于 GPIO 1 设备树里中断节点的语法…

OVN实验----L3互通

概述 物理拓扑 如上一个实验OVN实验----L2互通 逻辑拓扑 按照上个实验OVN实验----L2互通 的操作方式&#xff0c;再配置一组容器blue&#xff0c;网段192.168.2.0/24 配置完成后可以在central上ovn-sbctl show看到如下4个绑定接口 此时&#xff0c;red和blue两个网段内是可…

EasyTrans,一个注解搞定数据翻译,减少30%SQL代码量

介绍easy trans适用于3种场景1 有userId/idCardNo(身份证号码-唯一键场景) 需要 userName&#xff0c;无需联表查询。2 有gender code 0 需要 男。3 枚举指定属性给前端亮点1 缓存支持2 跨微服务翻译支持(User和Order 是2个不同微服务&#xff0c;order里面有userId 需要userNa…

strapi系列--如何自定义非界面化的接口,定制化自己的业务逻辑

为什么要进行后端定制呢&#xff1f; 在实际开发过程中&#xff0c;项目中有些需求是不需要创建界面化接口的&#xff0c;需要我们定制化自己的业务逻辑&#xff0c;那么我们该如何处理这个需求呢&#xff1f;本文以图文并茂的形式&#xff0c;定制一个我们自己的业务逻辑接口…

blender 应用物体变换的作用

编辑模式和物体模式操作的区别 旋转 在物体模式下旋转时物体旋转值会发生变换** 在编辑模式下旋转时物体不会发生变化** 缩放 在物体模式下缩放会导致缩放尺寸发生变化 在编辑模式下缩放时&#xff0c;缩放属性不会发生变化 应用物体变换 把物体模式下的缩放旋转变换应…

Android---TabLayout

目录 TabLayout TabItem ​编辑 演示效果的xml TabLayout TabLayout 在开发中一般作为选项卡使用&#xff0c;常与 ViewPager2 和 Fragment 结合起来使用。 常用属性&#xff1a; app:tabBackground 设置 TabLayout 的背景色&#xff0c;改变整个TabLayout 的颜色&#xf…

绪论的习题

刘佳瑜*&#xff0c;王越 *, 黄扬* , 张钊* (淮北师范大学计算机科学与技术学院&#xff0c;安徽 淮北) *These authors contributed to the work equllly and should be regarded as co-first authors. &#x1f31e;欢迎来到机器学习的世界 &#x1f308;博客主页&#xff1…

idea调试常用的快捷键

一、F7 步入调试&#xff0c;进入当前函数内部。 说明&#xff1a; 如果步入的是自己编的函数&#xff0c;可读性会好很多。 如果是系统函数&#xff0c;我个人目前水平&#xff0c;觉得很难读。而且idea系统已编写好的函数&#xff0c;除非是研究源码&#xff0c;否则感觉…

javaweb08 javaweb、tomcat、maven简介、servlet原理和实例、Mapping映射、请求转发和读取properties文件

文章目录一、javaweb简介二、Tomcat三、Maven四、Servlet简介和HelloWorld五、Servlet原理六、Mapping映射七、ServletContext八、请求转发九、读取资源文件properties一、javaweb简介 在java中&#xff0c;动态web资源开发的技术成为javaweb 人们访问到的任何一个网页和资源…

C语言字符串库函数模拟实现

字符串检验 strlen 函数原型 /// brief 返回给定空终止字符串的长度&#xff0c;即首元素为 str 所指&#xff0c;且不包含首个空字符的字符数组中的字符数 /// param str 指向要检测的字符串的指针 /// return 字符串 str 的长度 size_t strlen( const char *str );空终止字…

C语言实现通讯录静态版本

通讯录中首先要有人的信息&#xff0c;然后是存放多少个人的信息 再丰富一下通讯录的功能&#xff0c;例如增删查改、显示、排序。 我们分三个文件来实现。 1、实现简易的菜单&#xff0c;通讯录的整体逻辑 #include"contact.h"void menu() {printf("*****…

900页文档比对只需5分钟?鸿翼InWise文档比对,以人工智能撬动办公效率杠杆

在日常办公中&#xff0c;多份文件间的检查、纠错、复核工作不可避免&#xff0c;这类工作往往具有很强的重复性&#xff0c;占用了大量的工作时间。鸿翼InWise平台文档比对能够赋能企业极速完成海量文档、图片的高精度比对&#xff0c;以人工智能撬动企业生产力提升。 随着数字…

MySQL调优-MySQL索引深入总结

目录 MySQL索引深入总结 InnoDB中的索引复习 聚集索引/聚簇索引 问题&#xff1a;如果我们没有定义主键呢&#xff1f; 问题&#xff1a;分析一下B树三层和四层的性能差异&#xff1f; 辅助索引/二级索引 回表 问题&#xff1a;为什么我们还需要一次回表操作呢?直接把完…

亚马逊云科技:小鹏汽车拓展全球市场,跑出“加速度”

汽车产业变革走向下半场&#xff0c;智能汽车的市场份额之争也从国内走向国际&#xff0c;出海之战讲求速战速决&#xff0c;小鹏汽车携手亚马逊云科技拓展海外市场&#xff0c;完成海外布局。 扩大“鹏”友圈&#xff0c;迈进欧洲市场 近年来&#xff0c;小鹏汽车不断推进全…

数字人民币年度总结:支付变革未停、试点之风再起

文/尹宁出品/陀螺研究院数字人民币&#xff0c;无疑是近年来热度最高的词汇之一&#xff0c;作为我国法币的数字形式&#xff0c;至其出世伊始&#xff0c;不论是资金溯源的透明追踪、零售更新的消费用途&#xff0c;还是跨境结算的雄图大略&#xff0c;围绕其推广与意义的讨论…

关于 国产麒麟系统上长时间运行Qt程序根目录/下磁盘空间占用100%导致无法写入 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/128671382 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…