HashMap扩容机制
1.什么是resize:
resize就是重新计算容量;当我们不断的向HashMap对象里不停的添加元素时,HashMap对象内部的数组就会出现无法装载更多的元素,这是对象就需要扩大数组的长度,以便能装入更多的元素;当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组;就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。
2.什么时候需要resize():
当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值—即当前数组的长度乘以加载因子的值的时候,就要自动扩容。
扩容:(源码 661-662)
计算阀值:
1.第一次创建Hash表时:
2.对HashMap进行扩容时:
3.源码分析:
final Node<K,V>[] resize() {
//保存旧的 Hash 数组
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
//超过最大容量,不再进行扩充
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
//容量没有超过最大值,容量变为原来的两倍
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
//阀值变为原来的两倍
newThr = oldThr << 1;
}
else if (oldThr > 0)
newCap = oldThr;
else {
//阀值和容量使用默认值
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
//计算新的阀值
float ft = (float)newCap * loadFactor;
//阀值没有超过最大阀值,设置新的阀值
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
//创建新的 Hash 表
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
//遍历旧的 Hash 表
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
//释放空间
oldTab[j] = null;
//当前节点不是以链表的形式存在
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
//红黑树的形式,略过
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else {
//以链表形式存在的节点;
//这一段还是看下面的图解吧,搞了好久才懂得 ^_^
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
以链表形式存在的节点:
存在两个数他们的 Hash 值分别为:5,21 二进制形式分别为(0101,10101)。
若没有进行扩容时容量为 16,进行扩容之后的容量为 32
坐标点的计算(计算规则 :e.hash & (newCap - 1)):
没有进行扩容时:
可以看到两个Hash值所计算的坐标是相同的。
进行扩容之后:
可以看出经过扩容之后,两次计算的坐标出现了不同,但是第二个坐标点增加了 oldCap 个长度。
再看看 e.hash & oldCap 所计算出的结果:
可以看到当 e.hash & oldCap == 0 是,原来的坐标没有发生变化,e.hash & oldCap != 0 在原来坐标的前提下增加 oldCap 。
两条链表的连接过程:
在向表中连接的时候最后一个节点的下一个节点做空。
总结:
- 在对 HashMap 进行扩容时,阀值会变为原来的两倍;
- 在对HashMap进行扩容的时候,HashMap的容量会变为原来的两倍;
- 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。
- 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。
HashMap扩容机制的更多相关文章
- HashSet保证元素唯一原理以及HashMap扩容机制
一.HashSet保证元素唯一原理: 依赖于hashCode()和equals()方法1.唯一原理: 1.1 当HashSet集合要存储元素的时候,会调用该元素的hashCode()方法计算哈希值 1 ...
- 面试笔记--HashMap扩容机制
转载请注明出处 http://www.cnblogs.com/yanzige/p/8392142.html 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. 存放新 ...
- HashMap 扩容机制
引用于: http://www.cnblogs.com/hongdada/p/6024832.html HashMap: public HashMap(int initialCapacity, flo ...
- Java中HashMap扩容机制思考
1. HashMap在什么条件下扩容 判断HashMap的数组Size大小如果超过loadFactor*capacity,就要扩容. 相关的类属性: capacity:当前数组容量,始终保持 2^n, ...
- java源码--HashMap扩容机制学习
待完成 Java中hash算法细述 https://blog.csdn.net/majinggogogo/article/details/80260400 java HashMap源码分析(JDK8) ...
- 深入理解HashMap的扩容机制
什么时候扩容: 网上总结的会有很多,但大多都总结的不够完整或者不够准确.大多数可能值说了满足我下面条件一的情况. 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. ...
- HashMap底层结构、原理、扩容机制
https://www.jianshu.com/p/c1b616ff1130 http://youzhixueyuan.com/the-underlying-structure-and-princip ...
- 浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制
JAVA中的部分需要扩容的内容总结如下:第一部分: HashMap<String, String> hmap=new HashMap<>(); HashSet<Strin ...
- HashMap的扩容机制以及默认大小为何是2次幂
HashMap的Put方法 回顾HashMap的put(Key k, Value v)过程: (1)对 Key求Hash值,对n-1取模计算出Hash表数组下标 (2)如果没有碰撞,直接放入桶中,即H ...
随机推荐
- Hanlp中使用纯JAVA实现CRF分词
Hanlp中使用纯JAVA实现CRF分词 与基于隐马尔可夫模型的最短路径分词.N-最短路径分词相比,基于条件随机场(CRF)的分词对未登录词有更好的支持.本文(HanLP)使用纯Java实现CRF模型 ...
- 搭建Cordova + Ionic + WebStorm环境开发Web App应用
1. 下载并且安装Node.js(https://nodejs.org/en/) 2. 打开终端,安装cordova (如果安装失败或者卡住不动则重新安装) sudo npm install - ...
- Configure Virtual Serial Port Driver (vspd)注册表
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VSBC7\Ports\COM3COM4] “Port1”=”COM3” “Port2”=” ...
- AM软件中reconfig的方法
1.先进入admin 2.选数据库名: 比如这个HULLBLOCK/AFTBLOCKS 3. 选好数据库后在命令栏依次输入以下命令,输入每一行后回车一下: FROM DB HULLBLOCK/AF ...
- 如何在Java中获取键盘输入值
程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值的现成函数!Java没有提供这样的函数也不代表遇到这 ...
- springboot springcloud 热部署
本文转载自:https://www.cnblogs.com/moly/p/7978303.html 可以在配置修改某些文件重启,建议配置一个控制文件,只有此文件修改才会重启. 1. pom中添加: & ...
- 算法:整数与ip地址转换
直接上代码(不要直接拷贝,中间少了一行啊): #include <string> #include <iostream> using namespace std; ...
- pyqt5.8.2没有qt Designer和assistant exe
使用python3.6 pyqt5.8 eric6 创建完新的窗体后,弹出如下的错误: 解决方法: 1.安装pyqt5-tools 下载地址: https://pypi.python.org/pypi ...
- 峰Redis学习(7)Redis 持久化RDB方式
第一节:Redis 持久化介绍 redis所有的数据都存在内存中,所以速度非常快,但是一旦断电等情况,数据就没了.从内存当中同步到硬盘上,这个过程叫做持久化过程. 持久化操作,两种方式:rdb方式.a ...
- AWS机器学习初探(1):Comprehend - 自然语言处理服务
AWS机器学习初探(1):Comprehend - 自然语言处理服务 1. Comprehend 服务简介 1.1 功能 Amazon Comprehend 服务利用自然语言处理(NLP)来分析文本. ...