HashMap底层原理解析(面试心得)

1 为什么hashMap效率高?

  • 数组下标定位快,链表增删快。查询(数组定位,扫描一部分链表),增删是在链表上进行的,所以hashmap的效率高

2 组成

  • HashMap由数组+链表+红黑树(1.8之后加的)组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。

  • JDK8之后,如果哈希表单向链表中元素超过8个,那么单向链表这种数据结构会变成红黑树数据结构。当红黑树上的节点数量小于6个,会重新把红黑树变成单向链表数据结构。
    在这里插入图片描述

3 HashMap的put 和 get 的实现

3.1 map.put(k,v)实现原理

  • 将k v封装到Node(节点)对象中

  • 调用k 的hashCode()方法得出hash值

  • 通过哈希算法,将hash值转换成数组的下标,如果下标位置没有元素,就把node添加到这个位置上。如果有,就以链表的形式存放,此时会拿着k和链表上的每个节点的k进行equal,如果返回的都是false,将其添加到链表末尾,如果某个返回了true,此节点的value会被覆盖。

3.2 map.get(k)的实现

  • 调用k的hashcode计算hash值 通过哈希算法转换成数组的下标

  • 利用下标定位,没有链表,返回此位置上的value或者null,有链表,拿着k和链表上的每一个节点进行equals,如果返回的都是null,则get返回的是null。否则,返回对应节点的value。

4 扩容(resize) 什么时候扩容? 最消耗性能的点。

  • 当hashmap中的元素个数超过数组(16)大小*0.75(负载因子)时,就会进行数组扩容。
  • 在hashmap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。

5 Hash算法的实现(数组下标的算法&为什么数组长度是2的n次密)
在这里插入图片描述

  • 首先算得key得hashcode值,然后跟数组的长度-1做一次“与”运算(&)。看上去很简单,其实比较有玄机。比如数组的长度是2的4次方,那么hashcode就会和2的4次方-1做“与”运算。很多人都有这个疑问,为什么hashmap的数组初始化大小都是2的次方大小时,hashmap的效率最高,我以2的4次方举例,来解释一下为什么数组大小为2的幂时hashmap访问的性能最高。
    在这里插入图片描述

  • 看上图,左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,但是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就需要遍历这个链表,得到8或者9,这样就降低了查询的效率。同时,我们也可以发现,当数组长度为15的时候,hashcode的值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!

  • 所以说,当数组长度为2的n次幂的时候,不同的key算得的index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值