hashmap源码解读

hashmap源码解读

一、四个构造行函数

1.设置初始化大小,以及负载因子(threshold(阀值)=initialCapacity*loadFactor)
阀值:超出这个大小则会扩容。

在这里插入图片描述
最后,tableSizeFor():找到大于等于initialCapacity,且是2的次方,这个算法还是比较牛逼的,里面的逻辑是,初始化大小减去1,找到最大位数的1,将他无符号向右移动5次,如5,减去1 =4,4的二进制是0000 0100 经过这样一波操作 变为 0000 0111,最终加上1 变为 0000 1000 即2的三次方 :8,为什么要在最开始减去1呢,为了防止这个initialCapacity刚刚好是2的次方,如果不减1,就会是initialCapacity的二倍,但其实我们是要initialCapacity大小就行了。
在这里插入图片描述

2.设置初始化大小,负载因子默认0.75(经过测试在空间上和时间上比较平衡)
static final float DEFAULT_LOAD_FACTOR = 0.75f;
在这里插入图片描述
3.默认参数初始化大小为
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The default initial capacity - MUST be a power of two.
* 默认给出初始化大小16,且必须是2的次方
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

在这里插入图片描述
4.copy映射关系
在这里插入图片描述

二、存取数据

1.首先我们看put方法,对key进行hash
在这里插入图片描述

2.hash这里也是设计精妙,逻辑是:key的hash值高16位不变,低16位与高16位异或作为key的最终hash值。(h >>> 16,表示无符号右移16位,高位补0,任何数跟0异或都是其本身,所以key的hash值高16位不变。)
为什么要这样做呢,我们来看下取node的逻辑。
在这里插入图片描述

3.我们知道hashmap取数据快慢有一个很关键的因素在数据分布能不能很均匀,如果大部分数据取hashcode都是一样的,这样都放在一个了链表(超过8个则放在红黑树了)里面了,这样内部又需要一个遍历来判断key是否相等,这样的时间复杂度O(N^2),这是如何hash就是看我在取这个node的时候他下标如何hash的了。
这是下标hash逻辑。

n = table.length;
index = (n-1) & hash;

因为table的长度都是2的幂,因此index仅与hash值的低n位有关(此n非table.leng,而是2的幂指数),hash值的高位都被与操作置为0了。
假设table.length=2^4=16。
在这里插入图片描述
由上图可以看到,如果采用低位hash值,只有hash值的低4位参与了运算。
这样做很容易产生碰撞。所以在hashmap key的hash上,设计者将它的高16位和低16都取到,既不丢失性能又能将碰撞率降低。
在这里清清浅浅图片描述

4.在前面hashmap初始化的时候细心的朋友可能发现 this.threshold = tableSizeFor(initialCapacity);
这句不对啊不是应该 this.threshold = tableSizeFor(initialCapacity)*loadFactor,但其实在,hashmap构造方法中,并没有对table这个成员变量进行初始化,table的初始化被放到了put方法中,在put方法中会对threshold重新计算,这个put方法的大概逻辑是,在刚刚初始化之后的table值为null因此程序会进入到resize()方法中。而resize方法就是用来进行扩容的(稍后提到)。扩容后得到了一个table的节点(Node)数组,接着根据传入的hash值去获得一个对应节点p并去判断是否为空,是的话就存入一个新的节点(Node)。反之如果当前存放的位置已经有值了就会进入到else中去。接着根据前面得到的节点p的hash值以及key跟传入的hash值以及参数进行比较,如果一样则替覆盖。如果存在Hash碰撞就会以链表的形式保存,把当前传进来的参数生成一个新的节点保存在链表的尾部(JDK1.7保存在首部)。而如果链表的长度大于8那么就会以红黑树的形式进行保存。
在这里插入图片描述

三、resize扩容

resize分三个case:
1.使用默认构造方法初始化HashMap。从前文可以知道HashMap在一开始初始化的时候会返回一个空的table,并且thershold为0。因此第一次扩容的容量为默认值DEFAULT_INITIAL_CAPACITY也就是16。同时threshold = DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR = 12。

2.指定初始容量的构造方法初始化HashMap。那么从下面源码可以看到初始容量会等于threshold,接着threshold = 当前的容量(threshold) * DEFAULT_LOAD_FACTOR。

3.HashMap不是第一次扩容。如果HashMap已经扩容过的话,那么每次table的容量以及threshold量为原有的两倍。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值