HashMap源码分析

一、Hash表

拿hashMap举例,因为hashMap是hash表实现的,在jdk1.8之前,都是通过数组+链表来实现的hashmap,数组就是一个桶,存储元素和查找都要先找到桶的位置,就像是一个烤串的炉子,而桶的每个槽位需要通过key的hash来计算,再用hash值和数组长度-1做按位与运算,或者取模运算,就可以得到桶槽的位置,那么每个槽位存储的元素可以想象成不同数量的肉块,而位置相同的肉块是需要用签子将他们串起来,也就是图中看到的链表,是用来解决hash碰撞或者说hash冲突的
在这里插入图片描述
下面用表格总结一下hash的增删改查操作和原理
在这里插入图片描述

二、hash值怎么计算

2.1 为什么用hash计算

java中任何对象都有object.hashCode()
怎么计算hash
h ^ (h>>>7) ^ (h>>>4)
在这里插入图片描述

2.2 计算数组下标

index = h & (length-1)
等同于index = hash % n

三、如何添加键值对

hashmap,put源码解析,先看一下伪代码

put(K k,V v){
	hash(k)
	index= hash&(n-1)
}

一、计算key的hash,我们暂且忽略for循环中的逻辑,主要看addEntry方法,增加一个元素
在这里插入图片描述
二、addEntry方法中,首先根据key计算hash值,然后根据hash值传入indexFor方法,计算出数组下标bucketIndex,最后调用createEntry方法来创建元素
在这里插入图片描述
在这里插入图片描述

三、保存找到的bucketIndex位置的数组元素e,然后new Entry创建一个新的元素,next指针指向刚保存的e,此时相当于新增节点的next域指针指向了要插入位置的原来的节点
在这里插入图片描述
紧接着,看一下Entry的源码中,next还是一个Entry<K,V>,
在这里插入图片描述
截止到这里,我们就完成了链表中添加了一个节点,而且是在链表头节点添加的,速度很快,而key和hash算法的出现就是为了解决快速定位数组下标的问题,如果key相同,值不同,会采用链表的形式存储,也成为了hash冲突的解决方式之一:拉链法,下面我们理解一下什么是哈希冲突

在这里插入图片描述

四、hash碰撞|冲突

前边我们研究了通过Key的hash计算hash值,然后通过hash和数组长度按位与可以计算出数组的下标,那么如果不同的key计算出的下标相同,怎么办?往哪放?

jdk1.8之前hashmap源码都是采用数组+链表来实现,而链表就是为了解决hash冲突问题的,也成为拉链法。每当Put一个新节点的时候,不同的Key计算出的hash值相同,数组下标也相同,桶的位置确定了,这个时候新的节点就会插入到链表的头部,不仅将新的节点存储,而且效率达到最高,头插法的方式实现链表的元素新增

五、如何通过key找value

我们围绕以下这两个思考点来分析,问题便迎刃而解
1、怎么找数组位置?
2、怎么轮询找具体的元素?

问题1:看一下下面的伪代码,数组位置也就是数组下标,原理和put方法一模一样,都是通过hash(key),和hash&(n-1)来计算的

V get(Object key){
	hash(key)
	index= hash&(n-1)
}

问题2:我们来研究一下get方法,get(key)方法中会调用getEntry(key)
在这里插入图片描述
getEntry(key)方法中,重点看for循环中的逻辑,我们发现,查找元素的过程就是轮询链表元素的过程,indexFor很熟悉上文解释了它可以返回数组下标,table[数组下标]就是循环中访问的当前元素e节点,而if条件判断中的意思是如果该e节点的hash值和传入的key的hash值相同,并且key值也相同才会返回该e节点
在这里插入图片描述

六、为什么需要加载因子

6.1 为什么需要加载因子?

在极端的情况下,hash表中的数组下标如果都相同,那么,链表就是一个单链表,增删速度也最慢,为了减少冲突的发生,我们需要扩容,也就是扩大数组的长度来解决冲突

6.2 什么时候扩容呢?

这是就需要定义加载因子,源码默认是0.75,假如数组长度16,当16*0.75=12,也就是填充量>12的时候需要扩容

6.3 扩容会带来什么问题呢?

扩容也就是将数组的长度增加,下面举一个例子,来证明hash值改变,扩容以后get可能算出的位置有变化,所以,需要所有节点需要重新计算hash值,也就是rehash操作

如果hash=1
hash%15 = 1
扩容以后
hash%31 =1
但是如果hash =16
hash%15 = 1
扩容以后
hash%31=16

6.4 hashMap java8有什么改进

Hash冲突以后不再是采用链表来保存相同index节点,
相应的采用红黑树来保存冲突节点

在这里插入图片描述
节点查找优先级由O(n) -> 提高到O(logn)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕容野野

需要你的肯定

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值