HashMap面试

Posted zhangliang1726

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap面试相关的知识,希望对你有一定的参考价值。

1.你知道HashMap底层的数据结构是什么样的吗?

底层最核心的数据结构是数组,我们构造一个map,往里面放入数据,比如我们放入key为张三,value为测试数据,对张三计算出一个hash值,根据这个hash值对数组进行取模,就会定位到数组里的一个元素中去

2.jdk1.8中对hash算法和寻址算法是如何优化的?

还是以map.put("张三","测试数据")为例

对"张三"这个key计算它的hash值,会有一定的优化

hash算法优化

//JDk1.8以后的HashMap里面的一段源码

static final int hash(Object key){
    int h;
    return(key == null) ?0:(h=key.hashCode())^(h>>>16);
}

比如说:有一个key的hash值

1111 1111 1111 1111 1111 1010 0111 1100

0000 0000 0000 0000 1111 1111 1111 1111(右移16位:n>>>16)

异或结果:1111 1111 1111 1111 0000 0101 1000 0011

结果是二进制的表示,转换成一个int值,也就是代码里面的h,这个值就是hash值

我们可以看出,其实 (h=key.hashCode())^(h>>>16) 是把hash值的高16位和低16位进行了一个运算

寻址算法优化

(n-1)& hash ->数组里的一个位置  (n代表数组长度,此处n为16)

1111 1111 1111 1111 1111 1010 0111 1100

0000 0000 0000 0000 0000 0000 0000 1111 (n-1:也就是15的二进制)

1111 1111 1111 1111 0000 0101 1000 0011 (优化后的hash值)

取模运算,他是性能比较差一些,为了优化这个数组寻址的过程

hash&(n-1) ->效果是跟hash对n取模,效果是一样的,但是与运算的性能要比hash对n取模要高很多。那为什么要高很多了?这是一个数学问题,数组的长度会一直是2的n次方,只要他保持数组长度是2的n次方,hash对n取模的效果就和hash & (n-1)一样的,而后者的性能更高

如果我们拿没有经过优化的hash值进行与运算,其实高16位之间的运算是可以忽略的,核心点在于低16位的运算,hash值的高16位就没有参与到运算里面来

假设有两个hash值

1111 1111 1111 1111 1111 010 0111 1100

1111 1111 1111 1111 1110 1010 0111 1100

它们两个的低16位是一样的,高16位不同,由于高16位的运算参与可以忽略不计,所以就会造成困扰,那么经过优化后得到的hash值低16位就会有很大不同

总结

hash算法的优化:对每个hash值,在他的低16位中,让高低16位进行了异或,让他的低16为同时保持了高低16位的特征,尽量避免一些hash值后续出现冲突,大家可能会进入数组的同一个位置

寻址算法的优化:用与运算替代取模,提升性能

3.你知道hashMap是如何解决hash碰撞问题的吗?

hash冲突问题,通过链表加红黑树问题来解决

两个key,多个key,算出来的hash值与n-1运算后,定位出来的数组的位置还是一样的,这就是hash碰撞,那怎么解决了?

会在这个位置挂一个链表,这个链表里面放入多个元素,让多个key-value对,同时放在数组的一个位置里

我们get取的时候,如果定位到数组里发现这个位置挂了一个链表,此时遍历链表,从里面找到自己要的那个key-value对

如果只是链表的话,链表很长,那么遍历链表的时候,性能就会比较差,O(n);

所以有了一个优化,当链表的长度达到了一定的长度后,会把链表转换成红黑树,遍历一颗红黑树找一个元素,此时O(logn),性能会比链表高一些

4.说说HashMap是如何进行扩容的可以吗?

底层是一个数组,当这个数组满了以后,他就会自动进行默认二倍扩容,变成一个更大的数组,比如数组16位,就会扩容到32位

数组变大以后会进行一个rehash,重新把数组定位到32位数组里面的位置里面

以一个数组长度为16的Map举例

n-1    0000 0000 0000 0000 0000 0000 0000 1111

hash1 1111 1111 1111 1111 0000 1111 0000 0101

&结果 0000 0000 0000 0000 0000 0000 0000 0101  = 5(index = 5的位置)

 

n-1   0000 0000 0000 0000 0000 0000 0000 1111

hash2   1111 1111 1111 1111 0000 1111 0001 0101

&结果   0000 0000 0000 0000 0000 0000 0000 0101  = 5(index = 5的位置)

在数组长度为16的时候,他们两个hash值的位置是一样的,出现一个hash冲突的问题,用链表来处理

如果数组的长度扩容为32以后,重新对每个hash值进行寻址,也就是每个hash值跟新数组的length-1进行与操作

 

n-1    0000 0000 0000 0000 0000 0000 0001 1111

hash1 1111 1111 1111 1111 0000 1111 0000 0101

&结果 0000 0000 0000 0000 0000 0000 0000 0101  = 5(index = 5的位置)

 

n-1   0000 0000 0000 0000 0000 0000 0001 1111

hash2   1111 1111 1111 1111 0000 1111 0001 0101

&结果   0000 0000 0000 0000 0000 0000 0001 0101  = 21(index = 21的位置)

判断二进制结果中是否多出一个bit的1,如果没多,那么就是原来的index,如果多了出来,那么就是index+oldCap,oldCap就是原来的数组长度,通过这个方式,就避免了rehash的时候,用每个hash对新数组.length取模,取模性能不高,位运算的性能比较高

以上是关于HashMap面试的主要内容,如果未能解决你的问题,请参考以下文章

包含不同片段的HashMap(或ArrayList)

面试常用的代码片段

面向面试编程代码片段之GC

14道HashMap面试题了解一下?

头条面试之----HashMap原理

面试必考的HashMap概念