吊打面试官之HashMap

本文详细解析了HashMap的底层数据结构变化,从数组链表到引入红黑树,以及put、get方法流程,数组扩容机制,线程安全性问题。对比了jdk1.7与jdk1.8的实现差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HashMap的底层数据结构是什么?

         jdk1.7和jdk1.7前使用的是数组,链表

         jdk1.8和jdk1.8后使用的是数组,链表,红黑树

 

HashMap的put方法流程是怎么样的?

         1.先获取key的hash值

              

               注意:通过key获取hash值,直接获取hash值就可以了,但是这里把key的hash值的前16个字节和后16个字节用^来运算,是为什么呢?           使hash值更加随机,防止避免通过hash值计算出来的索引大概率的为一个索引值

 

           2.通过上一步获取的hash值计算索引

          

           通过源代码我们可以发现计算索引的公式为 (n-1) &  hash,   n为数组长度hashMap默认数组长度为16,hash为上一步计算出来的hash值

         HashMap中数组的长度默认是16,每次扩容1倍,这是为什么呢?

              通过上面的代码我们可以发现计算索引的值和hash值与数组的长度有关,通过&来计算

              16-1 =15 , 15的二进制值为   1111

              32-1 = 31 , 31的二进制值为   11111

              所有二的等次幂减1的二进制所有位置的值都为 1,获取的hash值是随机的,所以当hashMap的数组长度为2的等次幂时,计算出来的索引是随机的,减少了hash碰撞,提高了hashMap性能

          

          3.把数据存储到指定的索引位置中,如果该索引位置有值,就会在该索引位置形成一条链表,新增加的数据插入在原来的数据前面

                  jdk7是头插法,把新增加的数据插入在链表头部,头插法在多线程的情况下数组扩容,会导致循环链表的问题出现

                  jdk8是尾插法,把新增加的数据插入在链表最后面,尾插法避免循环链表这个问题

 

HashMap的get方法流程是怎么样的?

           1.先通过key获取key的hash值,通过计算获取元素所在数组的索引,获取数据,使用hashCode和equals方法来比较key是否一样,如果不一样遍历列表,再比较

 

HashMap的数组什么时候扩容?

        HashMap中有一个负载因子值为 0.75,当  HashMap中的元素个数* 0.75  =  HashMap中的数组长度时,HashMap就会扩容,数组扩大到原来的一倍长度

 

jdk7与jdk8中HashMap做了哪些改变?

        1.jdk8中添加了红黑树数据结构,当链表长度超过8时,会把链表转成红黑树,当链表长度小于等于6的时候会把红黑树转成链表

        2.jdk8中使用的是尾插法,jdk7中使用的是尾插法

 

HashMap是线程安全的吗?

         HashMap不是线程安全的,线程安全的map有HashTable和ConcurrentHashMap

         推荐使用ConcurrentHashMap它的效率比HashTable要高,它采用的是分段锁

        HashTable采用的是synchronized

             

欢迎大家关注我的微信公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值