您现在的位置是:首页 >技术教程 >HashMap 扰动函数、负载因子、扩容链表拆分网站首页技术教程
HashMap 扰动函数、负载因子、扩容链表拆分
简介HashMap 扰动函数、负载因子、扩容链表拆分
1.扰动函数
在jdk8中,hashmap有这样一段代码,他叫扰动函数,目的是优化散列效果
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
我们获取到的key.hashCode()哈希值,哈希的取值范围是[-2147483648, 2147483647],那我们能拿这个数来当作下表吗?很显然不太行,那就需要数组的大小做与运算,hashmap的初始数组大小是1 << 4 ,也就是16,那计算数组的下标同过与运算的方式获得,(16-1)&h。
1. 那么为什么我要使用这个扰动函数呢?我们不用扰动函数行不行?
使用扰动函数的目的就是为了优化散列,从而减少hash碰撞。
2. 那我们为什么选择数组长度要选择2的幂次方?
假如数组长度是8,那获取下标索引 (8-1)& h,数组长度就会出现一个0111除了高位以为都是1的特征,也是为了散列。
2.初始容量
- hashmap初始容量大小是16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
- 那我们都直到数组的长度是选择2的幂次方的,那假如我们传入的默认长度是17,那么就要找比初始值大的那个最小2的幂次方,2的5次幂32>17,那么传入17的默认容量大小就是32。
那么底层是有一段代码去支撑的:
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
这个n|=n>>>1,2,4,8,16这个右移主要是把右边都填上1,假如我们输入的是17
3.负载因子
负载因子决定了什么时候进行扩容,为什么不等把所有数组中所有下标都用完之后在扩容呢?其实就是为了减少hash碰撞,从而提高效率。当负载因子设置0.75的时候,假设初始容量是16,那么当增加到13个数的时候,就会进行扩容操作。
4.扩容链表拆分
为什么扩容,因为数组长度不足了。那扩容最直接的问题,就是需要把元素拆分到新的数组中。拆分元素的过程中,原jdk1.7中会需要重新计算哈希值,但是到jdk1.8中已经进行优化,不再需要重新计算,提升了拆分的性能,设计的还是非常巧妙的。
那么jdk1.8是如何做优化的呢?
假如原数组是16,现在扩容到32,那么原数组hash与增长出来的长度16做与运算,的出来的结果如果为0则下标不表,如果新位置就是原来的位置加16。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。