首页 > 编程语言 >HashMap源码分析(一)

HashMap源码分析(一)

时间:2022-11-30 15:35:10浏览次数:37  
标签:分析 Node hash HashMap tab next 源码 key null


之前有写到ArrayList的源码分析,欢迎大家点开我的头像查看

对于HashMap这个类大家一定不陌生,想必多多少少用过或者了解过,今天我来和大家谈谈HashMap的源码,JDK为1.8

继承AbstractMap类,实现map接口等等

HashMap源码分析(一)_HashMap

当你不设置它的容量时默认的容量大小  为16

HashMap源码分析(一)_链表_02

这个属性代表着它的最大容量,大小可以理解为2的30次方

HashMap源码分析(一)_链表_03

负载因子,如果构造方法没有指定负载因子的话默认0.75,也就是当它容量达到百分之75的时候扩容

HashMap源码分析(一)_初始化_04

这个属性代表着如果它的链表结构长度达到了8的时候链表的数据结构将会变成红黑树,提高效率

HashMap源码分析(一)_链表_05

如果链表结构长度小于6的时候又会从红黑树变成链表,因为当你长度就这么长的时候链表性能反而更好

HashMap源码分析(一)_链表_06

容器可以树化的最小表容量,可以理解为当它变成红黑树的最小表容量

HashMap源码分析(一)_初始化_07

在第一次使用的时候初始化

HashMap源码分析(一)_HashMap_08

保存缓存的set映射 遍历map的时候需要

HashMap源码分析(一)_HashMap源码分析_09

长度,不多说

HashMap源码分析(一)_HashMap_10

代表它被修改的次数

HashMap源码分析(一)_赋值_11

常用的属性就介绍到这里,接下来我们来详细看看HashMap是怎么样put添加数据的

put方法直接是返回putVal,那我们详细看看这个方法

HashMap源码分析(一)_链表_12

注释写在代码中

/**
*put方法的内部
* Implements Map.put and related methods
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent false, boolean evict true) {

Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果是空,也就代表是第一次put ,
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //生成初始化的长度
if ((p = tab[i = (n - 1) & hash]) == null) //如果值==null,则证明这个数据为null
tab[i] = newNode(hash, key, value, null); //hash为key通过hash方法的到 将tab的第0个值设置为null
else { //反之则不为空时
Node<K,V> e; K k; //创建一个node<>和 一个key类型的对象 k
if (p.hash == hash && //如果p的hash和key的hash一致并且p的key==k或者参数key不为空并且key equals k 则将p赋给e
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode) //如果p是TreeNode类型的则进入 putTreeVal方法
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) { //如果p.next为空,则新建一个node
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st //如果循环次数>=变成红黑树的要求数
treeifyBin(tab, hash); //调整表的大小
break;
}
if (e.hash == hash && //相同hash和key则停止遍历
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e; //将e赋值给p
}
}
if (e != null) { //如果e!=null,则表示map中的键有和put的键重复的,将会返回旧数据的value
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount; //更改次数+1
if (++size > threshold) //长度+1,如果长度大于需要扩容的大小时 resize初始化或加倍表格大小
resize();
afterNodeInsertion(evict); //节点插入后的一个回调方法
return null;
}
}

其中这个方法又调用了resize()方法

//初始化或加倍表格大小。如果为null,则分配符合字段阈值中保存的初始容量目标。 
//否则,因为我们正在使用2次幂扩展,所以每个bin中的元素必须保持在相同的索引处,
//或者在新表中以2的偏移量移动。

final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;//table为map初始化的node
int oldCap = (oldTab == null) ? 0 : oldTab.length; //如果为空则返回0,否则返回oldTab的长度
int oldThr = threshold;//threshold表示到达该数map将会扩容 例如刚开始新建为16*0.75
int newCap, newThr = 0;//newThr为要达到扩容的数 newCap为map的大小容量
if (oldCap > 0) { //如果map初始化的node大于0
if (oldCap >= MAXIMUM_CAPACITY) { //如果大于或者等于最大允许容量 1<<30
threshold = Integer.MAX_VALUE; //threshold 等于int的最大数值
return oldTab; //将map初始化的node返回
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && //如果 newCap==0或者大小正常 再将oldCap*2赋给newCap,判断这个newCap是否小于1<<30并且大于默认长度16
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold //newThr则为下一个调整扩容的2倍
}
else if (oldThr > 0) // initial capacity was placed in threshold //如果到达扩容数大于0,newCap得到到达扩容数的值
newCap = oldThr;
else { //零初始阈值表示使用默认值
newCap = DEFAULT_INITIAL_CAPACITY; //newCap为默认map大小16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); //newThr为默认将要到达则需要扩容的数
}
if (newThr == 0) { //如果将要扩容数newThr为0,
float ft = (float)newCap * loadFactor; //ft为map新容量*哈希表的加载因子 默认是0.75,但可以new对象时可以手动设置
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE); //当map新容量小于1<<30时并且ft小于1<<30时
返回ft不然为int最大值 newThr为将要达到扩容时数值 (第一次为12)
}
threshold = newThr; //将newThr赋值给达到将要扩容的数
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; //new一个容量为新大小的tab
table = newTab; //将新大小的tab赋值给 一个抑制的table
if (oldTab != null) { //如果旧tab不是空
for (int j = 0; j < oldCap; ++j) { //遍历旧tab
Node<K,V> e;
if ((e = oldTab[j]) != null) { //当前node不为空
oldTab[j] = null; //赋值为空
if (e.next == null) //如果下一个为空
newTab[e.hash & (newCap - 1)] = e; //&代表如果前面的为false后面的将不会执行
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}

HashMap主要是通过这两个方法进行put操作

 

 

 

标签:分析,Node,hash,HashMap,tab,next,源码,key,null
From: https://blog.51cto.com/u_15897407/5899827

相关文章

  • ArrayList的源码分析(一)
    我想大家既然能看到这篇文章我就不用解释Arraylist是啥了,简单点说就是一个动态对象数组,然后就这个集合的源代码拿出来给大家分析一下我的个人看法和收获,实例代码是jdk1.8......
  • ArrayList的源码分析(二)
    上篇文章给大家介绍了arraylist集合源码的一些属性和扩容方式add方法,接下来再和大家来聊聊这个集合的一些源码首先看看remove的方法,这个方法有两个,一个是根据下标删除对......
  • Mybatis源码分析(十五) - 缓存技术
    MyBatis包含一个非常强大的查询缓存特性,使用缓存可以使应用更快地获取数据,避免频繁的数据库交互 缓存查询图: 一级缓存(也叫应用缓存)一级缓存默认会启用,想要关闭一级缓存......
  • Mybatis源码分析(十三) - 关联查询之多对多
    我的理解是,多对多其实就是两个一对多。嵌套结果:示例代码:<selectid="selectUserRole"resultMap="userRoleInfo">selecta.id,a.user_name,a.real......
  • Mybatis源码分析(十四) - discriminator 鉴别器映射
    在特定的情况下使用不同的pojo进行关联,鉴别器元素就是被设计来处理这个情况的。鉴别器非常容易理解,因为它的表现很像Java语言中的switch语句discriminator标签常用的......
  • Mybatis源码分析(十七) - 源码包分析【日志模块】
    mybatis源码下载地址:​​https://github.com/mybatis/mybatis-3​​MyBatis源码导入过程:下载MyBatis的源码检查maven的版本,必须是3.25以上,建议使用maven的最新版本mybatis的......
  • Mybatis源码分析(二十一) - 核心流程分析
    mybatis核心流程三大阶段初始化阶段读取XML配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作代理阶段封装iBatis的编程模型,使用mapper接口开发的初始化......
  • Tomcat之tomcat架构分析
    Tomcat的目录结构 bin执行目录sh文件liux上的,bat文件windows上的confcatalina.policy 权限相关Permission,Tomcat是跑在jvm上的,所以有些默认的权限server.xml: Server节......
  • windows下编译调试 Elasticsearch 8.7.0 源码
    最近想从代码层面学习下ElasticSearch,于是下载代码并导入到idea中,开始一顿操作,gradle各种倒腾,还是没法直接从代码运行进程,最后选择了一种不那么直接的debug方法,远程......
  • Spring 框架的设计理念与设计模式分析
     ​​https://github.com/javahongxi​​Spring作为现在最优秀的框架之一,已被广泛的使用并有很多文章分析它。本文将从另外一个视角试图剖析出Spring框架的作者设计Spring......