面试必备技能:HashMap哪些技术点会被经常问到?

1.为什么用数组+链表?

数组是用来确定桶的位置,利用元素的key的hash值对数组长度取模得到.

链表是用来解决hash冲突问题,当出现hash值一样的情形,就在数组上的对应位置形成一条链表。ps:这里的hash值并不是指hashcode,而是将hashcode高低十六位异或过的。至于为什么要这么做,继续往下看。

2.hash冲突你还知道哪些解决办法?

比较出名的有四种(1)开放定址法(2)链地址法(3)再哈希法(4)公共溢出区域法

3.HashMap在什么条件下扩容?

如果bucket满了(超过load factor*current capacity),就要resize。

load factor为0.75,为了最大程度避免哈希冲突

current capacity为当前数组大小。

4.为什么扩容是2的次幂?

HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法;这个算法实际就是取模,hash%length。

但是,大家都知道这种运算不如位移运算快。

因此,源码中做了优化hash&(length-1)。

也就是说hash%length==hash&(length-1)

那为什么是2的n次方呢?

因为2的n次方实际就是1后面n个0,2的n次方-1,实际就是n个1。

例如长度为8时候,3&(8-1)=3 2&(8-1)=2 ,不同位置上,不碰撞。

而长度为5的时候,3&(5-1)=0 2&(5-1)=0,都在0上,出现碰撞了。

所以,保证容积是2的n次方,是为了保证在做(length-1)的时候,每一位都能&1 ,也就是和1111……1111111进行与运算。

5.知道hashmap中put元素的过程是什么样么?

对key的hashCode()做hash运算,计算index;

如果没碰撞直接放到bucket里;

如果碰撞了,以链表的形式存在buckets后;

如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树(JDK1.8中的改动);

如果节点已经存在就替换old value(保证key的唯一性)

如果bucket满了(超过load factor*current capacity),就要resize。

6.知道hashmap中get元素的过程是什么样么?

对key的hashCode()做hash运算,计算index;

如果在bucket里的第一个节点里直接命中,则直接返回;

如果有冲突,则通过key.equals(k)去查找对应的Entry;

  • 若为树,则在树中通过key.equals(k)查找,O(logn);
  • 若为链表,则在链表中通过key.equals(k)查找,O(n)。

7.你还知道哪些hash算法?

先说一下hash算法干嘛的,Hash函数是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间,使得数据容易保存。

8.知道jdk1.8中hashmap改了啥么?

  • 由数组+链表的结构改为数组+链表+红黑树。
  • 优化了高位运算的hash算法:h^(h>>>16)
  • 扩容后,元素要么是在原位置,要么是在原位置再移动2次幂的位置,且链表顺序不变。

最后一条是重点,因为最后一条的变动,hashmap在1.8中,不会在出现死循环问题。

9.为什么在解决hash冲突的时候,不直接用红黑树?而选择先用链表,再转红黑树?

因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。

当元素小于8个当时候,此时做查询操作,链表结构已经能保证查询性能。当元素大于8个的时候,此时需要红黑树来加快查询速度,但是新增节点的效率变慢了。

因此,如果一开始就用红黑树结构,元素太少,新增效率又比较慢,无疑这是浪费性能的。

10.我不用红黑树,用二叉查找树可以么?

可以。但是二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题),遍历查找会非常慢。

原文地址:https://www.cnblogs.com/shihaiming/p/11399430.html