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。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值