面试篇:HashMap

1.问:了解过红黑树吗?它有什么性质?

  答:红黑树是一种自平衡的二叉搜索树,他的查找,添加和删除的时间复杂度都为O(logN)。

        他有以下五种性质:

        1.红黑树的节点只有红色或者黑色两种颜色

        2.红黑树的根节点为黑色

        3.红黑树的叶子节点都是黑色的空节点

        4.红黑树的红色节点的子节点都是黑色

        5.从根节点到任意叶子节点的路径上,黑色节点数一致

2.问:什么是散列表?

答:散列表,又叫哈希表,是一个根据键来直接访问在内存存储位置的值的数据结构。

        它是由数组演化而来的,利用了数组可以按照下标进行随机访问数据的特征。

3.问:什么是哈希冲突(散列冲突)?

答:哈希冲突,指的是不同的key在进行hash运算的时候被映射到同一个索引位置

4.问:如何解决哈希冲突?

答:1.开放地址法,如果产生了哈希冲突,则以当前地址为基地址,根据再寻址的方法(探查序列),去寻找下一个地址,若发生冲突再去寻找,直至找到一个为空的地址为止。所以这种方法又称为再散列法。

        2.拉链法, 数组每个下标位置都称为一个桶,当产生hash冲突的时候,会把冲突元素存放入该下标的链表或者红黑树中。

5.问:HashMap为什么引入链表?

答:因为hashmap在进行put操作时,可能会产生hash冲突,这时候就需要把冲突的元素存放在对应数组下标的链表中。

6.问:HashMap为什么会引入红黑树?

答:因为如果链表长度大于8,其遍历速度较慢,此时可以引入红黑树,增加遍历的速度

7.问:为什么不一开始就使用红黑树?

答:因为树的维护成本比较大,毕竟红黑树在插入的时候要进行旋转,所以在链表短的情况下,会优先使用链表。

8.问:什么时候链表会转换成红黑树?

答:当链表的长度>8且数组的长度>64时,会把链表转换为红黑树

       当链表长度>8,数组长度<64时,会进行扩容

       当树的节点<6时,会退化成链表

9.问:jdk1.7和jdk1.8的HashMap有什么区别?

答:在jdk1.7时,采用的是数组+链表的方式,也就是会创建一个链表数组,每个数组的格子都是一个链表,当产生哈希冲突时,直接将冲突的元素添加到链表中即可。

        在jdk1.8时,在解决哈希冲突时,采用的是数组+链表+红黑树的方式,当链表的长度大于阈值(默认为8)且数组长度>64时,会转换成红黑树,以减少搜索时间。

10.问:讲一讲HashMap进行put操作的过程

答:在第一次进行put时,会初始化一个长度为16的数组,然后进行hash运算并根据索引插入对应位置。

       在非第一次进行put时,会先根据key计算索引,并判断该索引处是否为空(table[i] == null ?)

        如果是,则直接插入,如果不是,则判断key是否存在,存在则覆盖并插入,不存在则继续判断:

        (1). 如果当前索引处是红黑树,则进行红黑树的添加逻辑

        (2).否则就是链表,则遍历链表,看是否存在该key,如果存在则覆盖插入,不存在则在链表中插入新节点,最后判断链表长度是否>8,是的话则转换为红黑树,在红黑树中插入。

11.问: 讲一讲HashMap的扩容机制

答:第一次扩容时会判断oldcap>0 ? 不成立则说明是第一次添加数据,直接初始化数组容量为16,且阈值为16*0.75 = 12 (0.75为负载因子)。

        当判断oldcap>0成立,则进行扩容,扩容后的大小为原来大小的两倍。并且要遍历数组,把旧数组中的数据挪动到新数组中,具体为:

(1).对没有hash冲突的值,直接计算新数组的索引位置

(2).否则,如果是红黑树,则直接进行红黑树的添加逻辑

(3).如果是链表,则判断(e.hash & oldcap == 0) 是否成立,成立则留在原始位置,不成立则

         新位置 = 旧位置+原始容量(oldcap)

12.问:为什么负载因子是0.75?

答:这是根据概率学上的泊松分布来决定的,当负载因子为0.75时,时间和空间最佳权衡。

13.问:讲一下HashMap的寻址算法

答:(1). 首先计算对象的hashcode()值

        (2).再进行调用hash()方法,进行二次哈希,hashcode值右移16位再异或运算,让哈希更                    均匀。

        (3).最后再用(capacity - 1)& hash 得到索引。         (capacity是数组长度)

 

14.问:为什么HashMap的数组长度一定是2的次幂

答:1.计算索引时效率更高,当数组长度为2的n次幂时,计算索引可以使用位运算代替取模运算。

        2.当扩容时重新计算索引效率更高,如果 hash & oldcap == 0 ,则该元素留在原来的位置,

           否则该元素移动到  旧位置+oldcap 的位置   (oldcap为数组原始长度)

15.问:讲讲HashMap在jdk1.7的多线程死循环问题

答:在jdk1.7中,HashMap扩容时,进行数据迁移使用的是头插法,这样就有可能产生死循环

比如有两个线程,线程1和线程2:

线程1:读取到HashMap的数据,数组中某位置是个链表,线程1准备扩容时,线程2介入

线程2:它也读取到HashMap的数据,并直接进行扩容,因为是头插法,数据迁移后,链表内的顺序会颠倒,比如原本顺序是A->B,迁移后变成B->A,此时B的next指针指向A,结束。

线程1:此时他再继续执行,他先将A移到新链表,然后将B插入到新链表表头,因为受到线程2的影响,此时B的next指针指向A,A会继续回到链表头,就会形成B->A->B的死循环。

但是到了jdk8,对扩容算法进行了优化,数据迁移采用了尾插法,也就解决了多线程死循环的问题

  • 58
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值