首页 > 编程语言 >数据结构(Java):HashMap源码解析【JDK17】

数据结构(Java):HashMap源码解析【JDK17】

时间:2024-07-26 22:27:49浏览次数:15  
标签:Java HashMap 容量 数组 构造方法 源码 哈希 threshold 初始

1、整体总结


 

2、成员属性

table
哈希数组
DEFAULT_INITIAL_CAPACITY
哈希表默认容量值(16)
MAXIMUM_CAPACITY
最大容量值
DEFAULT_LOAD_FACTOR
默认负载因子
threshold
当前存放元素数量达到该值时就会触发数组扩容
TREEIFY_THRESHOLD
树化条件之一(转化为红黑树的阈值)
MIN_TREEIFY_CAPACITY
树化条件之一(转化为红黑树的阈值)
UNTREEIFY_THRESHOLD
解树化条件
size
哈希表中实际存储的元素个数

如图所示: 

3、构造方法

3.1 带参构造

带两个参数的构造方法,可以指定初始容量和负载因子

  1. 若指定的容量<0,则抛出异常 
  2. 若指定的容量 >最大容量,则容量为最大容量
  3. 若指定的负载因子<=0,抛出异常、
  4. 经过 tableSizeFor 方法,threshold得到的值为指定容量的2的次幂


带一个参数的构造方法:指定初始容量,负载因子为默认值:

(内部还是调用了带两个参数的构造方法)


带一个参数的构造方法:传入集合:


3.2 无参构造

无参构造,没有再调用其他构造方法,负载因子为默认值:

未做任何初始化,成员threshold的值为0) 


 到目前为止,哈希数组table还没有开辟内存,那么数组的空间是在哪里被开辟的呢?

我们猜想是在put方法中:


4、哈希数组初始空间的开辟

4.1 put方法(第一次添加元素)

 根据put方法,我们进入putVal方法:

因为初始时哈希表为空,进入resize方法为数组开辟空间。

4.2 resize方法(数值初始空间的开辟)

我们发现,初始时我们为数组开辟空间的大小取决于threshold的初始值,而threshold的初始值又取决于我们所调用的构造方法:

  1. 若调用的是无参构造,则threshold=0
  2. 若调用的是带参构造,且指定了初始容量大小initialCapacity,那么threshold的值就为指定容量的2的次幂的数值

 当 threshold = 0 时,哈希表开辟的初始容量为默认容量16

当 threshold != 0 时,哈希表开辟的初始容量就为threshold

我们做出以下总结:

  1. 当通过无参构造 构造HashMap时,第一次put为哈希表开辟的初始容量为默认容量16
  2. 当通过带参构造 构造HashMap时,若给出的指定初始容量为n,那么第一次put为哈希表开辟的初始容量为n的二次幂

 resize方法具体分析如下图:


5、put方法

上面讲了第一次put元素,数组为空时对数组空间的开辟。

接下来讲一讲在数组空间已有的情况下,如何进行元素的添加,我们发现:

  1. 当n的值为2的次幂时,(n-1)&hash 与 hash%len的值是相同的,这就是为什么在构造方法时,将threshold设置为2的次幂的原因。(计算得出新元素的位置)
  2. 判断插入位置是否哈希冲突,如果不冲突则直接创建新节点插入。
  3. 判断是否与原有元素的key值相同
  4. 判断插入的是红黑树还是链表,是红黑树则调用putTreeVal插入到红黑树中
  5. 如果是链表,新元素尾插入链表(JDK17采用尾插法)
  6. 当链表长度>=8,进入treeifyBin函数,进一步判断是否需要树化。
  7. 若进入了treeifyBin方法,还需判断数组长度是否>=64,若>=64才能树化。

故,树化的条件总结:链表长度>=8 && 数组长度>=64

put方法具体分析如下:

 

标签:Java,HashMap,容量,数组,构造方法,源码,哈希,threshold,初始
From: https://blog.csdn.net/2401_83595513/article/details/140693093

相关文章

  • Linux内核链表源码的简单操作
    一、Linux内核链表源码的获取下载系统源码的方法常见的有两种:第一种访问网站下载:kernel.org第二种输入Linux命令下载:sudoaptinstalllinux-source-5.15.0(一般这种下载的是当前系统所用到的系统源码版本)下载完之后在/usr/src中可找到系统源码的压缩包,可以解压......
  • SpringBoot源码初学者(二):SpringBoot事件监听器
    ps:真正适合阅读源码的新手来看的SpringBoot源码讲解,如果你真的想读懂SpringBoot源码,可以按照以下推荐的方式来阅读文章打开ide,打开SpringBoot源码,跟着文章一起写注释,写自己的注释不要过于纠结没讲到的地方,毕竟SpringBoot源码那么多,想全讲完是不可能的,只要跟着文章认真阅......
  • [Java并发]CountDownLatch
    CountDownLatch概述CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。有一点要说明的是CountDownLatch初始化后计数器值递减到0的时候,不能再复原的,这一点区别于Semaphore,Semaphore是可以通过release操作恢复信号量的。Co......
  • Java中的ETL工具选型与应用
    Java中的ETL工具选型与应用大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨Java中的ETL(Extract,Transform,Load)工具,帮助你了解如何选择和应用这些工具,以便高效地进行数据提取、转换和加载任务。ETL工具在数据工程和大数据处理中扮......
  • 体积计算器(三种语言)源码、效果图
    声明⚠️:英文、藏语翻译均来源网络,若不准确,请于本人联系,请勿抄袭!源码#A-1V=0#体积r=0#小半径R=0#大半径a=0#棱长&长b=0#宽h=0#高DMJ=0#底面积PI=3.14#π(保留两位小数)four_three=1/4*3#四分之三three_one=1/3*1#三分之一R......
  • java多线程把数据迁移到不同数据库中
    publicvoidsync_table_test_thread()throwsSQLException,InterruptedException{    longstart=System.currentTimeMillis();    SimpleDateFormatformat=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");    //获取要迁移oracle表数据库......
  • 零基础STM32单片机编程入门(二十二) ESP8266 WIFI模块实战含源码
    文章目录一.概要二.ESP8266WIFI模块主要性能参数三.ESP8266WIFI模块芯片内部框图四.ESP8266WIFI模块原理图五.ESP8266WIFI模块与单片机通讯方法1.硬件连接2.ESP8266模块AT指令介绍六.STM32单片机与ESP8266WIFI模块通讯实验1.硬件准备2.软件工程3.软件主要代码4.实验......
  • 学习Java的第十一天啦(2024.7.26)
    1.死锁的条件:死锁的产生还涉及到一些具体的条件,这些条件可以被看作是死锁产生的必要条件,包括:1.互斥条件:资源不能被多个进程或线程同时访问,即资源是互斥的。2.请求保持条件:进程或线程在请求资源时,已经持有其他资源,并且不愿意释放已经持有的资源。3.不可剥夺条件:已经分配给进......
  • [Java基础]Finally
    finally中的代码一定会执行吗?通常在面试中,只要是疑问句一般答案都是“否定”的,因为如果是“确定”和“正常”的,那面试官就没有必要再问了嘛,而今天这道题的答案也是符合这个套路。1.典型回答正常运行的情况下,finally中的代码是一定会执行的,但是,如果遇到以下异常情况,那么finall......
  • JAVA集中学习第二周学习记录(四)
    系列文章目录第一章JAVA集中学习第一周学习记录(一)第二章JAVA集中学习第一周项目实践第三章JAVA集中学习第一周学习记录(二)第四章JAVA集中学习第一周课后习题第五章JAVA集中学习第二周学习记录(一)第六章JAVA集中学习第二周项目实践第七章JAVA集中学习第二......