HashMap 扩容机制
引用于:
HashMap:

public HashMap(int initialCapacity, float loadFactor) {
//初始容量不能<0
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
//初始容量不能 > 最大容量值,HashMap的最大容量值为2^30
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//负载因子不能 < 0
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
// 计算出大于 initialCapacity 的最小的 2 的 n 次方值。
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
//设置HashMap的容量极限,当HashMap的容量达到该极限时就会进行扩容操作
threshold = (int) (capacity * loadFactor);
//初始化table数组
table = new Entry[capacity]; init();
}

在这里提到了两个参数:初始容量,加载因子。
这两个参数是影响HashMap性能的重要参数,其中容量表示哈希表中桶的数量,初始容量是创建哈希表时的容量,
加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,它衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。
对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;
如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。系统默认负载因子为0.75,一般情况下我们是无需修改的。
加载因子:
loadFactor
扩容:

void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold) // 这里是关键,一旦大于等于threshold的数值
resize(2 * table.length); // 将会引起容量2倍的扩大
}


void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity]; // 新的容器空间
transfer(newTable); // 复制数据过去
table = newTable;
threshold = (int)(newCapacity * loadFactor); // 重新计算threshold的值
}


void transfer(Entry[] newTable) {
// 保留原数组的引用到src中,
Entry[] src = table;
// 新容量使新数组的长度
int newCapacity = newTable.length;
// 遍历原数组
for (int j = 0; j < src.length; j++) {
// 获取元素e
Entry<K,V> e = src[j];
if (e != null) {
// 将原数组中的元素置为null
src[j] = null;
// 遍历原数组中j位置指向的链表
do {
Entry<K,V> next = e.next;
// 根据新的容量计算e在新数组中的位置
int i = indexFor(e.hash, newCapacity);
// 将e插入到newTable[i]指向的链表的头部
e.next = newTable[i];
newTable[i] = e;
e = next;
}
while (e != null);
}
}
}

通过上面的transfer方法可以看出,
e.next=newTable[i];
newTable[i]=e;
链表存储倒过来了,最先出来的会将其next指向null,后面的就指向前一个,当然数据只有原来的一部分。
===================================================================
随着HashMap中元素的数量越来越多,发生碰撞的概率就越来越大,所产生的链表长度就会越来越长,这样势必会影响HashMap的速度,
为了保证HashMap的效率,系统必须要在某个临界点进行扩容处理。
该临界点在当HashMap中元素的数量等于table数组长度*加载因子。
但是扩容是一个非常耗时的过程,因为它需要重新计算这些数据在新table数组中的位置并进行复制处理。
所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
问题:
当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。
在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。
如果条件竞争发生了,那么就死循环了。
HashMap 扩容机制的更多相关文章
- HashSet保证元素唯一原理以及HashMap扩容机制
一.HashSet保证元素唯一原理: 依赖于hashCode()和equals()方法1.唯一原理: 1.1 当HashSet集合要存储元素的时候,会调用该元素的hashCode()方法计算哈希值 1 ...
- 面试笔记--HashMap扩容机制
转载请注明出处 http://www.cnblogs.com/yanzige/p/8392142.html 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. 存放新 ...
- HashMap扩容机制
1.什么是resize: resize就是重新计算容量:当我们不断的向HashMap对象里不停的添加元素时,HashMap对象内部的数组就会出现无法装载更多的元素,这是对象就需要扩大数组的长度,以便能 ...
- 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 ...
随机推荐
- C/C++ linux下光标定位和清屏函数
printf("\033[47;31mhello world\033[5m"); 47是字背景颜色, 31是字体的颜色, hello world是字符串. 后面的\033[5m是 ...
- php中的可变变量、可变函数、匿名函数
1.可变变量 简而言之:获取一个普通变量的值作为这个可变变量的变量名. 如: $a = "hello"; $$a = " world"; /* $a 的值为&q ...
- .NET 环境中使用RabbitMQ RabbitMQ与Redis队列对比 RabbitMQ入门与使用篇
.NET 环境中使用RabbitMQ 在企业应用系统领域,会面对不同系统之间的通信.集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要.其次,系统中一般会有很多对实时性要求不高的 ...
- LA 5009 (HDU 3714) Error Curves (三分)
Error Curves Time Limit:3000MS Memory Limit:0KB 64bit IO Format:%lld & %llu SubmitStatusPr ...
- linux navicat 过期 解决办法
:~$ cd .navicat:~/.navicat$ rm *.reg:~/.navicat$ rm .update-timestamp:~/.navicat$ rm navicat.crontab ...
- div横向排列
在网页布局中,常常有几个div横向排列的需求,此时,可以采用浮动的方式: 1.左右浮动: <div class="main"> <div class=" ...
- layui动态渲染生成select的option值
脚本语言:设定默认值:直接拼接,然后根据返回值渲染select// 动态渲染脚本类型下拉框 // 1.发送ajax请求得到data // 2.将data渲染到页面上 function getDataL ...
- codeblocks中右键源文件没有Rename选项?
那是因为你右击的那个文件已经被CB的编辑器打开,关闭即可,你就能看到Rename选项了. 或者更简单,翻到Files那一栏,然后右击某个文件夹选择"Make root"即可,就跟w ...
- codeforces 651a oysticks
Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Submit Status De ...
- FreeMarker调用Java静态方法以及静态变量的方法
这里介绍下在类似 Spring+FreeMarker 的架构中如何在FreeMarker中访问Java中的静态方法以及静态变量. 一.首先为了方便以及可复用我们创建一个工具类 /** * FreeMa ...