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 。

两条链表的连接过程:

在向表中连接的时候最后一个节点的下一个节点做空。

总结:

  1. 在对 HashMap 进行扩容时,阀值会变为原来的两倍;
  2. 在对HashMap进行扩容的时候,HashMap的容量会变为原来的两倍;
  3. 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。
  4. 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。

HashMap扩容机制的更多相关文章

  1. HashSet保证元素唯一原理以及HashMap扩容机制

    一.HashSet保证元素唯一原理: 依赖于hashCode()和equals()方法1.唯一原理: 1.1 当HashSet集合要存储元素的时候,会调用该元素的hashCode()方法计算哈希值 1 ...

  2. 面试笔记--HashMap扩容机制

    转载请注明出处 http://www.cnblogs.com/yanzige/p/8392142.html 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. 存放新 ...

  3. HashMap 扩容机制

    引用于: http://www.cnblogs.com/hongdada/p/6024832.html HashMap: public HashMap(int initialCapacity, flo ...

  4. Java中HashMap扩容机制思考

    1. HashMap在什么条件下扩容 判断HashMap的数组Size大小如果超过loadFactor*capacity,就要扩容. 相关的类属性: capacity:当前数组容量,始终保持 2^n, ...

  5. java源码--HashMap扩容机制学习

    待完成 Java中hash算法细述 https://blog.csdn.net/majinggogogo/article/details/80260400 java HashMap源码分析(JDK8) ...

  6. 深入理解HashMap的扩容机制

    什么时候扩容: 网上总结的会有很多,但大多都总结的不够完整或者不够准确.大多数可能值说了满足我下面条件一的情况. 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. ...

  7. HashMap底层结构、原理、扩容机制

    https://www.jianshu.com/p/c1b616ff1130 http://youzhixueyuan.com/the-underlying-structure-and-princip ...

  8. 浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制

    JAVA中的部分需要扩容的内容总结如下:第一部分: HashMap<String, String> hmap=new HashMap<>(); HashSet<Strin ...

  9. HashMap的扩容机制以及默认大小为何是2次幂

    HashMap的Put方法 回顾HashMap的put(Key k, Value v)过程: (1)对 Key求Hash值,对n-1取模计算出Hash表数组下标 (2)如果没有碰撞,直接放入桶中,即H ...

随机推荐

  1. htmlcleaner使用及xpath语法初探

    一.HtmlCleaner使用: 1.HtmlCleaner HtmlCleaner是一个开源的Java语言的Html文档解析器.HtmlCleaner能够重新整理HTML文档的每个元素并生成结构良好 ...

  2. SDRAM初识

    SDRAM初识 1. 2. 3. 4. SDRAM分为bank地址和行列地址,看bank地址的位宽就可以推断出bank的数量,行列地址信号是集成在了一个信号中,是并行的. 5. 6. SDRAM与主机 ...

  3. mysql 闪回原理

    利用MySQL闪回技术恢复误删除误更改的数据 笔者相信很多人都遇到过忘带where条件或者where条件漏写了一个和写错了的情况,结果执行了delete/update后把整张表的数据都给改了.传统的解 ...

  4. SSH实现隧道功能穿墙

    Putty和SSH tunnel 目前寻求FQ的方式无非就几种: 寻找web代理(这个可以进我放置的在线代理进行测试) 自行寻找http/sock5代理(这个可以去网上搜索代理ip) vpnFQ(目前 ...

  5. Gradle详细解析***

    前言 对于Android工程师来说编译/打包等问题立即就成痛点了.一个APP有多个版本,Release版.Debug版.Test版.甚至针对不同APP Store都有不同的版本.在以前ROM的环境下, ...

  6. 关于java中Static关键字的加强理解

    static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键字的用法和平常容易误解的地方,最后列 ...

  7. 数据仓库专题18-数据建模语言IDEF(转载)

    1引言 IDEF的含义是集成计算机辅助制造(Integrated Computer-AidedManufacturing,ICAM)DEFinition.最初的IDEF方法是在美国空军ICAM项目建立 ...

  8. R语言数据挖掘相关包总结-转帖

    与数据挖掘有关或者有帮助的R包和函数的集合. 1.聚类 常用的包: fpc,cluster,pvclust,mclust 基于划分的方法: kmeans, pam, pamk, clara 基于层次的 ...

  9. PyQt5显示一个空白的窗口

    效果如下图: """ In this example, we create a simple window in PyQt5. """ # ...

  10. client家族属性

    在前面总结了offset家族属性和scroll家族属性,今天来总结一下client家族属性,同前面一样,client家族也包宽高和左上,具体的通过代码来区别这三大家族属性的不同. <!DOCTY ...