HashMap底层源码简单分析

Java容器–HashMap底层简单理解

一、存储介绍

HashMap底层实现采用了哈希表,数据结构中由数组和链表来实现数据的存储。

  • 数组:占用连续的空间。寻址容易,查询速度快。但是,增加和删除效率非常低。
  • 链表:占用空间不连续。寻址困难,查询熟读慢。但是删除和增加效率高。

哈希表就是结合两者有点而产生的,也就是哈希表的本质就是“数组+链表”。

image-20230410071407517

二、HashMap中常见的成员变量

/**
 * 默认初始容量
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16


/**
 * 最大的数组容量
 */
static final int MAXIMUM_CAPACITY = 1 << 30;


/**
 * 负载因子:决定数组在什么情况下进行扩容,当数组容量已经使用75%,
 *就需要扩容。
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;


/**
 *是否转换为红黑树的阈值
 */
static final int TREEIFY_THRESHOLD = 8;


/**
 * 将红黑树转化为链表的阈值
 */
static final int UNTREEIFY_THRESHOLD = 6;


/**
 *达到转化红黑树的数组长度
 */
static final int MIN_TREEIFY_CAPACITY = 64;
/**
 * The number of key-value mappings contained in this map.
 */
transient int size;


/**
 *桶数组
 */
transient Node<K,V>[] table;

image-20230410233118927

三、HashMap中存储元素的节点类型

  • Node节点类型

image-20230410235849151

我们常听说当HashMap中的链表长度大于8,且Hash桶的长度大于64时会将长度大于8的链表转化为红黑树。树的结构是什么样子的呢?

image-20230411000730954

transient是一个修饰符,用于告诉编译器在序列化(将对象转化为字符序列)时不需要持久化保存该字段的值。在反序列化(将字符序列转化为对象)时,该字符将初始化为其的值默值,引用类型为null,原始数据类型为0.

对于两种不同的节点为什么可以放入一个Node类型的桶数组中,原因是它们之间存在着继承的关系:

image-20230411001748052

image-20230411001945471

四、HashMap中数组的初始化

在JDK 1.8 的HashMap中对于数组的初始化是采用延时加载的初始化方式。通过resize()实现数组 的初始化处理。该方式即实现了数组的初始化化,也实现数组实现扩容处理。

何为延时初始化,也就是在new一个实例时候,它不会去初始化数组table而是等到第一个元素加入时,才进行初始化操作。

image-20230411002611969

在这里我们进入无参构造方法中看一眼,它只是对loadFactor 负载因子赋值一个默认的负载因子0.75f。

image-20230411002944882

进入HashMap的put方法中,可见也很简单,只是调用了一个putVal方法。

image-20230411003305367

进入putVal中

image-20230411004043651

进入resize中

image-20230411004853547

image-20230411062411376

五、HashMap中计算Hash值

  • 首先,获得key对象的hashcod,调用key对象的hashcodee()方法,获得key的hash值。
  • 这个hashcode计算出hash值(要求在【0,数组长度-1】),hashcode是一个整数,我们需要将其转化成【0,数组长度-1】的范围内,要求尽量均匀的分布在这个区间 内。

image-20230411063117799

还是从put方法进入

image-20230411063307922

image-20230411063742342

使用key的hashcode与其高16位进行异或运算,例如

image-20230411063849958

这里产生的hash值显然不在数组的范围内,也不是最终的hash值。

回到putVal中

image-20230411064610210

异或运算例子:

image-20230411064123267

六、添加元素

上面计算hash值时候第一次没有元素,就直接放入,现在计算出来的位置已经有元素了。

那么现在有元素了,我们怎么处理。

在putVal中,

image-20230411065034781

image-20230411065601413

如果hash值相同,但是value不同就会执行上图的else,遍历到链表的最后,将其挂载链表的最后。

七、数组扩容

还是从put开始进入putVal中

image-20230411070649110

image-20230411071228190

image-20230411071440433

七、数组扩容

还是从put开始进入putVal中

七、数组扩容

还是从put开始进入putVal中

image-20230411070649110

image-20230411071228190

image-20230411071440433
这就是主要过程的原理的理解!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明天码上有钱啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值