HashMap相关面试题

76 阅读3分钟

1.说一下HashMap的实现原理?

难易程度:3

出现程度:5

1)底层使用hash表数据结构,即数组+(链表|红黑树)

2)添加数据时,计算key的值确定元素在数组中的下标

a.key相同则替换

b.不同则存入链表或红黑树中

获取数据通过key的hash计算数组下标获取元素

2.hashmap的jdk1.7和jdk1.8有什么区别?

jdk1.8之前采用的拉链法,数组+链表

jdk1.8之后采用数组+链表+红黑树,链表长度大于8且数组长度大于64则会从链表转化为红黑树

3.hashmap的put方法的具体流程

难易程度:4

出现频率:5

1)判断键值对数组table是否为空或者为null,否则执行resize()进行扩容(初始化)

2)根据键值key计算hash值得到数组索引

3)判断table[i]==null,条件成立,直接新建节点添加

4)如果table[i]==null,不成立

4.1 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value

4.2 判断table[i]是否为treeNode(红黑树),如果是红黑树,则直接在树中插入键值对

4.3 遍历table[i],链表的尾部插入数据,然后判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,遍历过程中若发现key已经存在直接覆盖value

5)插入成功后,判断实际存在的键值对数量size是否超过了最大容量threshold(数组长度*0.75),如果超过,进行扩容(resize)。

4.讲一讲hashmap的扩容机制

image.png

1)在添加元素或者初始化的时候,都需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次扩容都是达到了扩容阈值(数组长度*0.75,也就是12);

2)大于12的时候进行扩容,每次扩容的时候,都是扩容之前容量的2倍

3)扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新数组中。

(3.1)没有产生hash冲突的节点,则直接使用 e.hash&(newCap - 1) 计算新数组的索引位置。

(3.2)如果是红黑树,走红黑树的添加

(3.3)如果是链表,则需要遍历链表,可能需要拆分链表,判断(e.hash&oldCap)是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上。

ps: oldCap:旧的容量 newCap:新的容量

5.hashmap的寻址算法

难易程度:4

出现频率:4

1)计算对象的hashCode()

2)再进行调用hash()方法进行二次哈希,hashcode值右移16位(扰动算法)再异或运算,让哈希分布更为均匀,减少哈希冲突。

3)最后(capacity - 1)&hash得到索引(代替了取模,效率更高一些)

6.为啥hashmap的数组长度一定是2的n次幂?

1)计算索引时效率更高:如果是2的n次幂可以使用位与运算代替取模;

2)扩容时重新计算索引效率更高;hash & oldCap ==0的元素留在原来位置 ,否则新位置 = 旧位置 + oldCap