HasMap的知识点

1.Jdk1.7之前,hashmap就是链表+数组;Jdk1.8之后hashmap的数据结构变成了红黑树,数组和链表。(很大程度上解决了hash冲突)

2.在JDK1.8中当单个链表的长度超过8的时候,在扩容时会自动转换成红黑树的数据结构;当红黑树的节点小于6个的时候,数据结构会从红黑树变成链表;


        (为什么选用红黑树:提高查询效率;极端情况下查询效率也不会太慢;t频繁的插入和删除操作中,红黑树的总体性能更优;提高空间效率)

3.  HashMap 基于 Hash 算法实现的

  1. 当我们往HashMap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数 组中的下标
  2. 存储时,如果出现hash值相同的key,此时有两种情况。
  • 如果key相同,则覆盖原始值;
  • 如果key不同(出现冲突),则将当前的key-value放入链表中
  • 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值

4.hashmap的扩容机制

扩容就是HashMap底层的resize()方法 , 在两种情况下会执行 :

  1. 初始化HashMap集合, 第一次往里面存元素的时候会执行resize()方法 , 初始化底层的数组(默认容量是16) , 如果构造方法中传入了初始容量参数, 那么初始长度为大于传入初始容量的第一个2的n次幂
  2. 当HashMap集合中存入的键值对数量超过阈值(0.75)的时候会进行扩容 , 每次扩容容量为之前的2倍

在1.7 中,扩容之后需要重新去计算其Hash值,根据Hash值对节点位置进行重新分配

在1.8版本中,则是根据在同一个桶的位置中进行判断(e.hash & oldCap)是否为0,如果为0 则留在当前位置不动 , 如果不为0 则要移动到原始位置+增加的数组大小这个位置上 , 这也是为什么HashMap扩容容量为2的N次幂的一个原因

为什么HashMap的数组长度一定是2的次幂?

  1. 计算索引时效率更高:如果是 2 的 n 次幂可以使用位与运算代替取模运算, 效率更高
  2. 扩容时重新计算索引效率更高: hash & oldCap == 0 的元素留在原来位置 ,否则新位置 = 旧位置 + oldCap

为什么扩容阈值设置为0.75 ?

  • 选择扩容阈值为0.75的原因是为了在空间占用与查询时间之间取得较好的权衡
  • 大于这个值,空间节省了,但链表就会比较长影响性能
  • 小于这个值,冲突减少了,但扩容就会更频繁,空间占用多




     

5.多线程下Hash Map会有什么问题?
        在jdk1.7中,Hashmap在多线程操作过程中可能存在死循环的情况,因为在jdk1.7中hashmap是数组加链表,采用的是头插法。
        举个例子:线程A读取到当前的hashmap数据,数据中一个链表,在准备扩容时, 此时线程B介入,他也同时对这个map进行扩容操作。因为是头插法,链表会倒过来,由原来的AB变成BA,扩容完成后线程B退出,执行结束。此时线程A继续执行,这个时候A线程会将A插入新的链表,然后B插入链头,由于线程A的原因,B的next是指向A,即B->A->B,形成了死循环;
具体解析原因:
https://segmentfault.com/a/1190000024510131

        在jdk1.8中引入了尾插法,来防止死循环的发生,但是还是会导致数据丢失。
具体解析原因:

       为了避免这种情况,我们一般使用线程安全的concurrentHashMap;

6.hashmap的hash函数设计是怎么设计?
         hash 函数是先拿到通过 key 的 hashcode,是 32 位的 int 值,然后让 hashcode 的高 16 位和低 16 位进行异或操作。

7.jdk1.7和jdk1.8中都会导致数据丢失
        

        JDK1.7版本和JDK1.8版本都会出现并发数据丢失问题 :

        多线程情况下 , 多个线程操作多个key , 多个key的hash值相同 , 在HashMap底层会首先判断该Key对应的索引位置是否存在元素 , 如果不存在元素就会创建新的node , 将node放入到对应的索引位置

这个时候 : 第一个线程判断元素不存在, 创建了node , 在存入到数组之前 , 第二个线程执行了 , 也判断元素不存在, 也会创建node , 之后将元素存入数组 , 这样两个线程都将元素存入到数组的同一个位置, 就会有一个线程的数据丢失

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值