阿里面试题:说说HashMap的扩容过程?
这是一道阿里的面试题,考察你对HashMap源码的了解情况,废话不多说,咱们就直接上源码吧!
jdk 1.7 源码
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];//创建一个新数组
boolean oldAltHashing = useAltHashing;
useAltHashing |= sun.misc.VM.isBooted() &&
(newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean rehash = oldAltHashing ^ useAltHashing;//是否需要重新计算hash值
transfer(newTable, rehash);//将oldTable的元素迁移到newTable
table = newTable;//将新数组重新赋值
//重新计算阈值
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {//遍历oldTable迁移元素到newTable
while(null != e) {//①处会导致闭环,从而导致e永远不为空,然后死循环,内存直接爆了
Entry<K,V> next = e.next;
if (rehash) {//是否需要重新计算hash值
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];//①
newTable[i] = e;//①
e = next;//①
}
}
}
jdk 1.8 源码(比较长,慢慢品哈)
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; //扩容两倍的阈值
}
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"})
//创建一个newCap大小的数组Node
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)
//如果旧数组中e后面没有元素,则直接计算新数组的位置存放
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)//如果是红黑树则单独处理
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { //链表结构逻辑,解决hash冲突
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;
}
//原索引+oldCap放入数组中
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;//jdk1.8优化的点
}
}
}
}
}
return newTab;
}
总结
jdk1.7扩容是重新计算hash;jdk1.8是要看看原来的hash值新增的那个bit是1还是0好了,如果是0则索引没变,如果是1则索引变成"原索引+oldCap".这是jdk1.8的亮点,设计的确实非常的巧妙,即省去了重新计算hash值得时间,又均匀的把之前的冲突的节点分散到新的数组bucket上
jdk1.7在rehash的时候,旧链表迁移到新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,但是jdk1.8不会倒置
阿里面试题:说说HashMap的扩容过程?的更多相关文章
- Java HashMap的扩容
最近博主参加面试,发现自己对于Java的HashMap的扩容过程理解不足,故最近在此进行总结. 首先说明博主德Java为1.8版本 HashMap中的变量 首先要了解HashMap的扩容过程,我们就得 ...
- Java面试题之HashMap阿里面试必问知识点,你会吗?
面试官Q1:你用过HashMap,你能跟我说说它的数据结构吗? HashMap作为一种容器类型,无论你是否了解过其内部的实现原理,它的大名已经频频出现在各种互联网Java面试题中了.从基本的使用角度来 ...
- 透过面试题掌握HashMap【持续更新中】
本文主要是自己阅读了HashMap和ConcurrentHashMap源码及一些Java容器类相关的博客后,找了一些很多面经中涉及到的Java容器相关的面试题,自己全部手写的解答,也花了一些流程图,之 ...
- 深入理解HashMap的扩容机制
什么时候扩容: 网上总结的会有很多,但大多都总结的不够完整或者不够准确.大多数可能值说了满足我下面条件一的情况. 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. ...
- HashMap的扩容机制---resize()
虽然在hashmap的原理里面有这段,但是这个单独拿出来讲rehash或者resize()也是极好的. 什么时候扩容:当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值---即当前数组 ...
- HashMap的扩容机制以及默认大小为何是2次幂
HashMap的Put方法 回顾HashMap的put(Key k, Value v)过程: (1)对 Key求Hash值,对n-1取模计算出Hash表数组下标 (2)如果没有碰撞,直接放入桶中,即H ...
- HashMap初始化容量过程
集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生.在日常开发中,我们经常会像如下方式以下创建一个HashMap: Map&l ...
- 双系统Ubuntu分区扩容过程记录
本人电脑上安装了Win10 + Ubuntu 12.04双系统.前段时间因为在Ubuntu上做项目要安装一个比较大的软件,导致Ubuntu根分区的空间不够了.于是,从硬盘又分出来一部分空间,分给Ubu ...
- jdk7和8中关于HashMap和concurrentHashMap的扩容过程总结,以及HashMap死循环
题外话:为什么要hashcode进行spread? 充分使用key.hashCode()的高16位信息,保证hash分布更分散, 扩容操作是新建2倍于原表大小的新表,并将原表结点拷贝一份放在新表中,对 ...
随机推荐
- python 基础之格式化输出
字符占位符%s #_cvvh:"chenxi" #date: 2019/6/24 print ('chhjg') # 格式化输出 name = input("Name:& ...
- React脚手架less的安装
最近在用react.js 结合蚂蚁金服的 Ant Design Mobile 做一个单页面的应用程序,遇到了一个很棘手的问题——那就是 react脚手架不支持less,看了不少优秀博主如何在react ...
- Python中文编码问题(字符串前面加'u')
中文编码问题是用中文的程序员经常头大的问题,在python下也是如此,那么应该怎么理解和解决python的编码问题呢? 我们要知道python内部使用的是unicode编码,而外部却要面对千奇百怪的各 ...
- jquery的正则表达式
正则表达式 位置: ^ 开头 $ 结尾 次数: * 0或多个 + 1或多个 ? 0或1个 {n} 就是n个 {n,} 至少n个 {n,m} ...
- 『jQuery』.html(),.text()和.val()的概述及使用--2015-08-11
如何使用jQuery中的.html(),.text()和.val()三种方法,用于读取,修改元素的html结构,元素的文本内容,以及表单元素的value值的方法 本节内容主要介绍的是如何使用jQu ...
- CPL学习笔记(二)
数组 数组(array) 声明通用格式为: typeName arrayName[arraySize]. arrayName指定元素数目,必须为整型常量或const值.(不能是变量) int a[3] ...
- python入门:py2.x里面除法或乘法这么写就可以计算小数点后面结果
#!/usr/bin/env python # -*- coding:utf-8 -*- #py2.x里面除法或乘法这么写就可以计算小数点后面结果,更精确future(未来,译音:非忧车) divis ...
- Python3 pymsyql 连接读取数据
import pymysql conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='t ...
- 【结构型模式】《大话设计模式》——读后感 (12)在NBA我需要翻译?——适配器模式
适配器模式:将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能在一起工作的 那些类可以在一起工作了[DP] UML类图: 简单模拟一下代码: //已存在的.具有 ...
- F查询与Q查询
F查询 如果我们要对两个字段的值做比较,那该怎么做呢? Django 提供 F() 来做这样的比较.F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值. # 查询评论 ...