Java之HashMap系列--HashMap扩容的原理
原文网址:Java之HashMap系列--HashMap扩容的原理_IT利刃出鞘的博客-CSDN博客
简介
说明
本文介绍Java的HashMap是如何扩容的。
重要大小
类 |
初始容量 |
最大容量 |
扩容时倍数 |
加载因子 |
底层实现 |
HashMap |
2^4 |
2^30 |
n * 2 |
0.75 |
Map.Entry |
HashSet |
2^4 |
2^30 |
n * 2 |
0.75 |
HashMap<E,Object> |
HashTable |
11 |
Integer.MAX_VALUE - 8 |
n*2 + 1 |
0.75 |
Hashtable.Entry |
HashMap中,哈希桶数组table的长度length大小必须为2的n次方(非质数),这是一种非常规的设计,常规的设计是把桶的大小设计为质数。相对来说质数导致冲突的概率要小于非质数,具体证明可以参考此文,Hashtable初始化桶大小为11,就是桶大小设计为质数的应用(Hashtable扩容后不能保证还是质数)。
HashMap采用这种非常规设计,主要是为了在取模和扩容时做优化。为了减少冲突,HashMap定位哈希桶索引位置时,也加入了高位参与运算的过程。
何时扩容
HashMap是懒加载,构造完HashMap对象后,若没用 put 来插入元素,HashMap不会去初始化或者扩容table,此时table是空的。扩容有如下场景:
- 首次调用put方法时,HashMap会发现table为空然后调用resize方法进行初始化。
- 非首次调用put方法时,若HashMap发现size(数组大小)大于threshold(阈值)(当前数组的大小乘以加载因子的值),则会调用resize方法进行扩容。
- 链表长度大于8 且数组长度小于64 会进行扩容。(链表长度大于8 且数组长度大于等于64,会转化为红黑树)。
数组是无法自动扩容的,所以只能是换一个更大的数组去装填以前的元素和将要添加的新元素。
resize()概述
- 判断扩容前的旧数组容量是否已经达到最大(2^30)了
- 若达到则修改阈值为Integer的最大值(2^31 - 1),以后就不会扩容了。
- 若没达到,则修改数组大小为原来的2倍
- 以新数组大小创建新的数组(Node<K, V>[])
- 将数据转移到新的数组(Node<K, V>[])里
- 不一定所有的节点都要换位置。
- 比如:原数组大小为16,扩容后为32。若原来有hash值为1和17两个数据,他们对16取余都是1,在同一个桶里;扩容后,1对32取余仍然是1,而17对32取余却成了17,需要换个位置。
- 对应的代码为:if ((e.hash & oldCap) == 0) 若为true,则不需要换位置。
- 比如:原数组大小为16,扩容后为32。若原来有hash值为1和17两个数据,他们对16取余都是1,在同一个桶里;扩容后,1对32取余仍然是1,而17对32取余却成了17,需要换个位置。
- 不一定所有的节点都要换位置。
- 返回新的Node<K, V>[] 数组
源码
HashMap#resize()
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4
static final int MAXIMUM_CAPACITY = 1 << 30;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
transient Node<K,V>[] table;
int threshold;
final float loadFactor;final Node<K,V>[] resize() {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; // double threshold}else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else { // zero initial threshold signifies using defaultsnewCap = 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"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;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 { // preserve orderNode<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) { // 重点1:判断节点在resize之后是否需要改变在数组中的位置if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);// 重点2:将某节点中的链表分割重组为两个链表:一个需要改变位置,另一个不需要改变位置if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;
}
Java之HashMap系列--HashMap扩容的原理相关推荐
- Java之List系列--ArrayList扩容的原理
原文网址:Java之List系列--ArrayList扩容的原理_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Java的ArrayList是如何进行扩容的.即:扩容的机制. 重要大小 类 初 ...
- Java之HashMap系列--ConcurrentHashMap的原理
原文网址:Java之HashMap系列--ConcurrentHashMap的原理_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java中的ConcurrentHashMap的原理. JDK7与J ...
- [Java 8 HashMap 详解系列]7.HashMap 中的红黑树原理
[Java 8 HashMap 详解系列] 文章目录 1.HashMap 的存储数据结构 2.HashMap 中 Key 的 index 是怎样计算的? 3.HashMap 的 put() 方法执行原 ...
- 【Java集合学习系列】HashMap实现原理及源码分析
HashMap特性 hashMap是基于哈希表的Map接口的非同步实现,继承自AbstractMap接口,实现了Map接口(HashTable跟HashMap很像,HashTable中的方法是线程安全 ...
- java源码系列:HashMap底层存储原理详解——4、技术本质-原理过程-算法-取模具体解决什么问题
目录 简介 取模具体解决什么问题? 通过数组特性,推导ascii码计算出来的下标值,创建数组非常占用空间 取模,可保证下标,在HashMap默认创建下标之内 简介 上一篇文章,我们讲到 哈希算法.哈希 ...
- 深入Java集合学习系列:HashMap的实现原理
2019独角兽企业重金招聘Python工程师标准>>> HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和 ...
- Java面试系列--HashMap
最近看到Java相关的面试时,看到一篇关于HashMap的相关面试知识点,感觉蛮好的!现在的面试点不会围绕某个知识让面试者去详细阐述,而会通过一些系列的相关问题去让你阐述,进而形成从点到线,再由线到面 ...
- Java集合篇:HashMap原理详解(JDK1.7及之前的版本)
(本文有关HashMap的源码都是基于JDK1.6的) 摘要: HashMap是Map族中最为常用的一种,也是 Java Collection Framework 的重要成员.本文首先给出了 Hash ...
- Java之HashMap系列--JDK7与JDK8的HashMap的区别
原文网址:Java之HashMap系列--JDK7与JDK8的HashMap的区别_IT利刃出鞘的博客-CSDN博客 简介 本文介绍JDK7与JDK8的HashMap的区别. JDK7与JDK8的Ha ...
最新文章
- springCloud(22):Eureka总结提升
- 网游服务端php5.1时间戳格式化,php格式化时间戳显示友好时间的简单示例
- ${}和#{}的区别
- pycharm替换和查找文件中所有相同代码方法
- #1403 : 后缀数组一·重复旋律 (可重叠最长重复K次子串问题)
- pytest+allure生成漂亮的报告+显示
- sql 查询优化小计
- WordPress主题:Zibll子比主题 V4.0 绿色版
- ideahtml标签不提示_仓储物流加速,电子标签亮灯拣选系统的优势
- 三星Galaxy S21 FE现身Geekbench:骁龙888+6GB运存
- 单片机原理及应用姜志海pdf_单片机原理及应用.pdf
- Mac安装Xcode
- java 随机抽取数组内容_工具类:随机抽取数组或集合中的几个不重复元素
- 数据架构选型必读:2021上半年数据库产品技术解析
- 简单6步,手把手搭建MinDoc文档库
- Google map API:查询地理位置和经纬度信息示例
- HBase MapReduce MultiTableInput首次测试
- YY频道美化,模板修改工具【免费】可以闪动
- 一线顶级互联网公司offer的成功经验【转自IT面试】
- 第二章:minio单机版,使用客户端备份文件
热门文章
- html下拉复选框联动,HTML : CheckBox 复选框成组联动(JavaScript)
- 关于type_info与typeid
- 什么是 reactor 模式
- draw.io diagrams 画图 插入latex数学公式
- 3D美术人员Technical Artist(TA技术美术)的学习之旅(1)
- 《C语言程序设计》第4版 何钦铭、颜晖主编 课后习题答案 第12章 习题12
- 手撕深度学习中的优化器
- js+css实现鼠标点击时出现小心心
- 计算机windows前台运行图标,获取 window任务栏已经打开应用程序窗口(也就是任务管理器中前台进程)的图标...
- 介绍一个查看TCP连接的工具TCPView