HashMap 高频面试题

高频题目
(1)装填因子,负载因子,加载因子为什么是0.75?

  • 本质上是时间和空间的问题。
  • 比如装填因子设置为1:空间利用率得到了很大的满足,很容易碰撞,产生链表->查询效率低。
  • 再比如装填因子设置为0.5:碰撞的概率低,扩容,产生链表的几率低,查询效率高,空间利用率太低。
  • 经过大量的实验论证使用0.75是比较平衡的。

(2)HashMap 的长度为什么是 2 的幂次方?

  • 采用二进制位操作 &,相对于%能够提高运算效率
  • hash%length==hash&(length-1) 的前提(即使用&代替%的前提) 是 length 是 2 的 n 次方;
  • 可以减少hash冲突;
  • 扩容移动元素会比较方便;

(3)HashMap 和 Hashtable 的区别?

  • 线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!因为HashTable 性能不行);
  • 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
  • 对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;HashTable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。

补充一个面试场景:

  • 面试官:对HashMap了解吗?

  • 我:了解过

  • 面试官:你先说一下啥叫Hash吧?

  • 我:Hash 就是通过特定的算法将一个任意长度的输入映射成固定长度的输出。

  • 面试官:那这个过程会不会遇到一些问题?

  • 我:是会有一些问题的,比如不同的value值通过我们的Hash算法可能会得到相同的key,也就是我们常说的Hash冲突问题。

  • 面试官:那有办法避免Hash冲突吗?

  • 我:… 目前根据我的了解好像不能

  • 面试官:如果让你考虑一个Hash算法你会从哪些方面进行考虑?

  • 我:相同的输入可以得到相同的输出,而且这个过程的效率也要得到保证;对输入做出轻微的改动,输出肯定要改变的;不能根据输出的key逆推出输入的value,然后数据分布尽量均匀,也就是尽量散列。

  • 面试官:行,我们回归主题,HashMap的数据接口大致讲一下?

  • 我:分为jdk 1.8前后,1.8之前是数组加链表,1.8之后是数组加链表加红黑树;我接下来都以1.8为例,它的数据单元是Node结构,里面有key,value,hash,还有指向下一节点的next指针。
    在这里插入图片描述
    脑海中浮现出当时看过的源码

  • 面试官:HashMap的初始长度是多少?

  • 我:16

  • 面试官:那这个初始长度为16的散列表(数组)是啥时候创建的?

  • 我:实际执行 put 方法的时候创建的(有点懒加载的感觉)

  • 面试官:负载因子知道吗?默认是多少?有什么用?

  • 我:默认是0.75,主要是用来计算扩容阈值的,比如总共是16,那16*0.75=12,达到12就要扩容了。

  • 面试官:链表转红黑树要达到什么条件?

  • 我:首先链表长度要达到8,其次当前散列表数组的长度要达到64,否则会优先考虑扩容而不是转成红黑树。

  • 面试官:嗯嗯,node对象有一个hash字段,这个hash是key的hashcode()返回值吗?

  • 我:不是的,是经历了一个扰动函数,简单说就是讲hashcode()的返回值无符号右移16位,然后与原值进行异或运算。
    在这里插入图片描述

  • 面试官:为什么要这么做?

  • 我: 增大随机性,混合高低位增大随机性,减少碰撞概率。

  • 面试官: 讲一下HashMap put的过程吧!

  • 我:嗯,前面讲到经过扰动函数的到hash,然后实际插入数组下标还要 hash&(len-1)计算一下,如果是第一次进行put操作会调用扩容初始化数组,如果有冲突了向链表中插入数据,如果冲突十分严重了(链表长度达到8,数组长度超过64了,已经转红黑树了),向红黑树插入数据。再详细说一下就是,put时看一下有没有hash冲突没有直接new Node()放在数组中,有冲突看一下key是不是相同的,相同进行覆盖,不相同遍历一下尾插,然后判断是否需要转红黑树扩容啥的。

  • 面试官:能讲一下插入红黑树的过程吗?

  • 我:这方面没有太深入了解,然后我把红黑树的特点说了一下(是平衡二叉树,由黑色节点和红色节点组成,根节点是黑色,一个红色节点有两个黑色子节点)

  • 面试官:为啥1.8的时候加入了链表转红黑树?

  • 我:因为链表长度太长后查询太慢时间复杂度是O(n),红黑树查找效率会快很多时间复杂度是O(logn)

  • 面试官:HashMap 扩容后数组怎么迁移的?

  • 我: 对于null,和没有发生hash冲突的就没啥好说的了,重点是迁移链表,在jdk1.8后就不需要重新计算key的位置了,根据 hash&oldcap(扩容前数组长度)如果是0不用位置不变,如果是1加原数组长度即为新的位置,相当于原来的链表被拆成两个部分,在这个过程中是有四个指针,第一个指向是结果是0链表的开始节点(也可以叫做低位节点),第二个指向结果是0的链表的结束节点(也可以叫做高位节点),
    第三个指向是结果是1链表的开始节点(也可以叫做低位节点),第四个指向结果是1的链表的结束节点(也可以叫做高位节点),然后为0的指向原本的数组位置,为1的指向新位置(原来位置加原来数组长度)。然后再说一下红黑树,红黑树内部节点并没有舍弃next字段,其实内部还维护了一份链表,只是查询的时候不去用,可以利用这个链表快速进行拆分,过程和链表基本一致,就是要注意小于6时红黑树要转回链表。

先写到这里,回来写一下ConcurrentHashMap 的面试题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值