什么是HashMap?面试毕问

本文参照了公众号“程序员小灰”中的"什么是HashMap?"一文,加上了自己的理解,如果有侵权请联系我。给大家强烈安利这个公众号,以漫画的方式清晰的解释了很多知识点。初次写博客,意在学习和交流,期待大家的指正。

什么是HashMap?

HashMap是实现Map接口的实现类,是以Key-value形式存储数据的数据结构。在JDK1.7中,HashMap的底层是用哈希数组+链表实现的。键值对也叫做Entry实体,这个Entry实体包含了四个属性。

Entry实体:
  • key–对应了键
  • value–值
  • hash–hashcode的值
  • next–指向了下一个Entry实体

HashMap每次自动或者手动扩容的长度必须是2的幂,HashMap的默认长度是16。

HashMap的主要方法由put():
put()方法的作用是将元素添加到哈希数组中。
添加过程:

  1. 使用index=hashcode(key)&(length-1);
  2. index决定了元素在哈希数组中的位置。
  3. 查找到哈希数组中index位置是否已经存在Entry。
  4. 如果没有则把元素添加到数组中
  5. 如果有调用重写后的equals()对比两个元素是否相同
  6. 如果相同则覆盖掉原来的元素
  7. 如果不相同,则采用头插法,将元素插到链表中当作头节点,Entry的next属性指向原来的元素。

get()作用是根据key值将HashMap中的元素取出。

  1. 把元素的key值先进行Hash映射(index=hashcode(key)&(length-1);)得到index
  2. 取出index位置上对应的元素
  3. 如果index位置上对应的不只有一个Entry实体,那么就从链表的头部开始遍历,知道找到相应的Entry。

注意:遍历链表的时间复杂度是O(n)有链表中的元素个数决定,在JDK1.8中对此进行了优化,HashMap的底层实现变成了数组+红黑树+链表的形式,只有当链表中的元素个数超过8时有红黑树代替链表,时间复杂度变成了O(logn),并且在1.8之后采取就是尾插法了。

问题一:为什么使用1.8改用插法?

答:HashMap在多线程的情况下,会出现死循环。

问题二:为什么默认长度是16,扩容的长度是2的幂?

答:这主要是为了服务于key的hash算法,从key映射到index需要进行hash运算,在底层采用的是index=hashcode(key)&(length-1),采用了位运算,使用length-1的以16为例,换算成二进制1111。再与key的hash值做与运算(与运算:符号两边换成二进制后相同位置同为1结果才为1,否则为0)。
例子:“A”:“wxw”
“A”为key换算之后的二进制是110111011100010110
则有:110111011100010110&1111=0110换算成十进制就是6,则
key为“A”的键值对会存在哈希数组的6位置上。
也就是说决定了元素的位置的因素是key经过hash运算后的值最后几位决定的。

问题三: 碰撞的元素何时树化?

答:在默认情况下,当链表长度大于8并且,元素个数大于64时才会树化。

处于高并发下的HashMap

由于HashMap不是线程安全的,所以处于刚并发下的HashMap会存在一些问题,比如存储的时候会形成环状链表,在进行读操作时候造成死循环。

HashMap的Rehash机制

因为HashMap的长度有限,在经过大量的添加元素的时候,HashMap就会饱和,发生hash碰撞的几率就会大大提高,进而影响程序的性能。所以为了避免这种情况,HashMap需要扩展长度,也叫做Resize。
影响Resize的因素有两个:
Capacity:HashMap的当前长度(必须是2的幂)
LoadFactor:HashMap的负载因子,默认值是0.75f
衡量Resize的条件是
HashMap.size()>=Capacity * LoadFactor

Resize的扩容实际上是需要以下两个步骤:
扩容
创建一个新的Entry空数组,长度是原来的2倍
2.ReHash
遍历原来的数组把元素重新Hash到新的数组中为什么要重新Hash呢?因为长度扩大以后,Hash的规则也随之改变。
让我们回顾一下Hash公式:
index = HashCode(Key) & (Length - 1)
当原数组长度为8时,Hash运算是和111B做与运算;新数组长度为16,Hash运算是和1111B做与运算。Hash结果显然不同。

既然HashMap不是线程安全的,那么又没有线程安全的Map集合呢,当然有HashTable就是,但是HashTable采用了加锁的方式来保证线程安全,但是性能就大大降低了。那么有没有不降低性能又能保证线程安全的集合能,有ConcurrentHashMap。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值