Hashmap深究

Hashmap底层首先就是基于数组实现,后面就可以围绕数组进行解释。

Hash算法的优化:

// jdk1.8之后的原代码

Return (key==null) ?0 :(h=key.hashcode())^(h >>>16))

1111 1111 1111 1111 1111 1010 0111 1100  key.hashcode

0000 0000 0000 0000 1111 1111 1111 1111  h >>>16

^

1111 1111 1111 1111 0000 0101 1000 0011  转为int值返回

为什么这样处理呢?异或是结果值得低16位同时保证了高16位与低16位的特征,可以再后续的&运算都可以参与到运算之中,减少hash冲突。

寻址算法:

(n-1)&hash  得到数据的一个位置 n约定为16   取模运算的他是性能差了一些,为了优化寻址算法,hash &(n-1)效果和hash对n的取模效果一样,但是于运算性能比hash对n取模高,数学问题,数组的长度为2的次方的时候,只要保持数组长度为2的n次方

0000 0000 0000 0000 0000 0000 0000 1111 (n-1)的二进制

1111 1111 1111 1111 0000 0101 1000 0011 (优化后的二进制)

1111 1111 1111 1111 1111 1010 0111 1100 (没有优化的二进制)

&

则就是高16位实际上没有参与到运算里面,全部都是低16位的运算参与了。但是优化后的hash值保证了高低16位都参与了运算,从而减少了hash碰撞。

 

寻址算法的优化:用与运算替代取模,提升性能。

 

Map.put和map.get 算出得hash值,到寻址算法找到一个数组得位置,把key-value放进数组,或者从数组中取出来。但是如果两个key算出的位置是一样的,那么就涉及到了hash碰撞,那么这个时候就会在这个位置挂一个链表,把这个链表里面放入元素,如果get的时候,只需要定位到这个位置的链表,遍历链表,从里面找出自己需要的值。链表很长,那么性能越差(时间复杂度位O(n))

 

如果这个链表的长度达到了一定的长度之后,把链表转为红黑树,遍历一颗红黑树找到一个元素,时间复杂度位O(logn)。条件:当链表长度超过8的时候,转为红黑树,当红黑树的长度小于6的时候,转为链表。

 

 

hash桶中存放的链表长度概率  随着长度的增加而减小

hashmap中的源码注释

 

(二) 为什么到8转为红黑树  到6转为链表

TreeNodes(红黑树)占用空间是普通Nodes(链表)的两倍,为了时间和空间的权衡。

节点的分布频率会遵循泊松分布,链表长度达到8个元素的概率为0.00000006,几乎是不可能事件.

为什么转化为红黑树的阈值8和转化为链表的阈值6不一样,是为了避免频繁来回转化。

数据扩容:

重新扩容之后,会再次进行&运算,判断二进制结果中是否多一个bit的1,如果没有,那么就是原位置,否则就是index+oldgap,通过这个方式就避免了rehash的时候,每个hash对length取模。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值