HashMap源码分析2:扩容
本文源码基于JDK1.8.0_45。
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 threshold
newCap = oldThr;
//未指定容量初始化,采用默认的容量和阈值
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//未指定新的阈值时用容量计算阈值(可以看到在JDK1.8的源码中,只有指定容量的初始化时才会进入下面的逻辑)
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;
//以上是创建新数组的逻辑,下面再来看HashMap中原来存的数据怎么重新分配到新的数组中
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 order
Node<K,V> loHead = null, loTail = null;//数组下标不需要更改的节点放在这个链表里
Node<K,V> hiHead = null, hiTail = null;//数组下标需要更改的节点放在这个链表里
Node<K,V> next;
do {
next = e.next;
//以容量为16为例,则计算下标的方式是用哈希值与15做与运算。15的二进制为1111,即二级制数保留低4位的值
//假设当前扩容容量为32,则要与31做与运算。31的二级制为11111,即保留二进制数低5位的值
//如果哈希值与当前容量即16(二进制10000)做与运算等于0,表示该哈希值在新增的第五位为0,扩容后不影响其下标的计算
//因此这部分数据在新的数组中位于相同的位置
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
//与上述相反,如果与运算结果为1,表示其哈希值有影响,且下标移动的位置刚好是oldCap的值
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;
}
//下标的为原数组下标 + oldCap
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
本文同样忽略了扩容时红黑树的实现,这些内容将在之后介绍。
HashMap源码分析2:扩容的更多相关文章
- 【JAVA集合】HashMap源码分析(转载)
原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...
- Java中HashMap源码分析
一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtab ...
- JDK1.8 HashMap源码分析
一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...
- 【Java】HashMap源码分析——常用方法详解
上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...
- 【Java】HashMap源码分析——基本概念
在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...
- Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现
视频通过以下四个方面介绍了HASHMAP的内容 一. 什么是HashMap Hash散列将一个任意的长度通过某种算法(Hash函数算法)转换成一个固定的值. MAP:地图 x,y 存储 总结:通过HA ...
- Java源码解析——集合框架(五)——HashMap源码分析
HashMap源码分析 HashMap的底层实现是面试中问到最多的,其原理也更加复杂,涉及的知识也越多,在项目中的使用也最多.因此清晰分析出其底层源码对于深刻理解其实现有重要的意义,jdk1.8之后其 ...
- HashMap源码分析(史上最详细的源码分析)
HashMap简介 HashMap是开发中使用频率最高的用于映射(键值对 key value)处理的数据结构,我们经常把hashMap数据结构叫做散列链表: ObjectI entry<Key, ...
- HashMap 源码分析 基于jdk1.8分析
HashMap 源码分析 基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table; //这里维护了一个 Node的数组结构: 下面看看Node的数 ...
- 源码分析系列1:HashMap源码分析(基于JDK1.8)
1.HashMap的底层实现图示 如上图所示: HashMap底层是由 数组+(链表)+(红黑树) 组成,每个存储在HashMap中的键值对都存放在一个Node节点之中,其中包含了Key-Value ...
随机推荐
- Hadoop Hive概念学习系列之hive的数据压缩(七)
Hive文件存储格式包括以下几类: 1.TEXTFILE 2.SEQUENCEFILE 3.RCFILE 4.ORCFILE 其中TEXTFILE为默认格式,建表时不指定默认为这个格式,导入数据时会直 ...
- [译]libcurl错误码
CURLcode Almost all "easy" interface functions return a CURLcode error code. No matter wha ...
- (1)指针、引用、const限定符
自己看书时的一些理解,可能有错误的地方.随着指针的使用增多,会不断修改这篇文章的内容,过去错误的会用划线划去后保留. 1.对引用.指针.常量引用.指向常量的指针.常量指针的理解 //对引用.指针.常量 ...
- Appium基于python unittest自动化测试并生成html测试报告
本文基于python单元测试框架unittest完成appium自动化测试,生成基于html可视化测试报告 代码示例: #利用unittest并生成测试报告 class Appium_test(uni ...
- Java运行报错问题——Picked up JAVA_TOOL_OPTIONS: -agentlib:jvmhook
http://blog.csdn.net/xifeijian/article/details/8830933 上述这个朋友博文提醒,可能是因为其他软件添加了JAVA_HOME的路径造成冲突.但他支持删 ...
- PAT甲级考前整理(2019年3月备考)之二,持续更新中.....
PAT甲级考前整理之一网址:https://www.cnblogs.com/jlyg/p/7525244.html,主要总结了前面131题的类型以及易错题及坑点. PAT甲级考前整理三网址:https ...
- 基于spring 3.0mvc 框架的文件上传实现
Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框 ...
- ERwin逻辑模型
1.自动排序 Format>>Preferences>>Layout Entire Diagram CA ERwin
- day02python
''' 列表 定义:在[]内,可以存放多个任意类型的值,并以逗号隔开. 一般用于存放学生的爱好,课堂的周期等等... ''' students=['钱垚','李小龙','张全蛋','赵铁柱'] pri ...
- JavaScript 中实现 sleep
来自推特上 Windows 故障分析的笑话 图片来源:me.me 推上看到的笑话,Windows 故障分析的实现. 然后想起来 JavaScript 中如何实现这个 sleep() 函数让代码暂停指定 ...