14道HashMap面试题了解一下?

Posted 高级JAVA指南

tags:

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

对于HashMap无论是平常开发业务代码还是封装第三方开源框架,都是使用非常广泛的。其优秀的哈希存储机制也是像阿里、蚂蚁金服等大厂java面试中几乎必问的问题之一,因为透过你对HashMap源码设计的理解,可以立竿见影地知道你对java基础技术研究的热情以及学习能力、思维的缜密等等。

下面主要以java7 HashMap源码以及最下方参考文章为依据,列举HashMap相关的14个问题,这些问题无论面试官问与不问,了解一下总是对工作或对自己的提升有益处的,当然你也可以拿来问别人。阅读时建议结合HashMap源码阅读可能效果会更佳。

下面贴一张HashMap内部结构图然后就步入正题了:

  1. HashMap put方法设计思路?

a)key为null时存储在table[0] (链表中已有元素则寻找key为null的节点,覆盖value)

b)根据key的hash计算Entry的hash(低位异或)

c)根据Entry的hash计算Entry在table中的索引 (Entry的hash与table长度按位与)

d)根据索引获取Entry链表

e)遍历Entry链,如果找到Entry的hash相等并且key的equals为true,则覆盖value(key不覆盖)

f)如果没找到equals的key,则先按需扩容,再添加到Entry链表顶端

2.HashMap get方法设计思路?

a)key为null则从table[0]链表中查找key为null的节点并返回其value

b)根据key的hash计算Entry的hash(低位异或)

c)根据Entry的hash和table的长度获取Entry链表

d)遍历Entry链表寻找与Entry的hash相等并且key的equals为ture的Entry,如果找到了则返回Entry的value,否则返回null

3.HashMap 中key的hashcode相同时如何存储?

这种情况也称为哈希冲突。如果两个key的hashcode相同,那么equals不一定为true,但根据hashcode计算出来的table索引必然是相等的,这时候会遍历table[i]链表去逐个查找hash相等并且key相等或key的equals方法成立的Entry,如果找到则覆盖其value,如果没有找到则将新的key-value对存到table[i]链表顶端,并把next指向原来的链表顶端。

4.HashMap 中key相同时如何存储?

如果两个key相同,这意味着这两个key的hashcode相等并且equals成立。首先根据hashcode计算出Entry在table数组中的索引必然是相等的,然后遍历table[i]链表必然能找到hash相等并且key相等或key的equals方法成立的Entry,然后覆盖其value。

5.HashMap中 key为null的节点如何存储?

key为null的节点存在table[0]链表的顶端(如果该位置原来有Entry则覆盖其value)。

6.自定义类作为HashMap的key, 需要注意什么?有哪些现成的类适合作为HashMap的key?

需要注意:

a.需要保证key的hashcode不能变,即同一实例上多次调用hashCode方法必须返回相同的int值(否则可能出现存进去的key-value在查询时找不到,关于这一点可参考之前我写的一篇通用方法规约的文章)。

b.只要重写了equals方法,就必须重写hashcode方法(关于这一点可以参考equals方法和hashcode方法的关系)。

适合做Map的key的类有String,Enum,基本数据类型的包装类如Integer,Long等。

7.HashMap的扩容机制,扩容时机,HashMap的加载因子默认是多少?起什么作用?

HashMap的扩容机制加载因子有关,默认loadFactor=0.75。每当往HashMap中put新的key-value对时,都会进行扩容检查,如果当前容量达到容量阈值(即当前最大容量*加载因子)则容量扩充至原来的2倍,否则不扩容。

8.HashMap的初始容量默认是多少?其容量为何是2的幂?如何合理设置HashMap的初始容量?

HashMap的初始容量默认为16.

a)容量为2的幂,之所以为2的幂是因为根据hash计算索引时要计算h & (length-1), 而计算机中与2的幂按位与的计算速度更快(可以亲手测试一下从0到1亿分别于15和16按位与哪个更快,我测试的结果显示与16按位与要比与15按位与快2%)。容量的计算方式为从1开始不断左移,直到得到大于initialCapacity的数字即为HashMap真正的容量。

b)考虑到HashMap的扩容机制,我们应该在创建HashMap时指定其容量,避免多次扩容造成不必要的时间开销(如果Map容纳的数据量较少如为个位数则可以忽略初始容量的设置,但数据量大时设置合理的容量可以节省不少时间)。

c)如何合理设置HashMap的初始容量应该根据其加载因子(loadFactor)和HashMap预计容纳的数据量来计算。HashMap阈值(threshold) = capacity * loadFactor,假设HashMap实际容纳的数据量为阈值,那么capacity = threshold / loadFactor.

d)举个列子,已知Map中要存储1000个key-value,loadFactor = 0.75,那么capacity = threshold / loadFactor = 1000 / 0.75 = 1334, 取比1334大的2的幂那么capacity = 2048.

9.HashMap默认的加载因子(loadFactor)为何是0.75?

如果loadFactor太小则往Map中put数据时就需要更早地进行扩容,空间利用率较低。如果loadFactor太大则可能会导致链表更长,相应的查询起来更慢。设置为0.75是JDK大神们经过研究、测试后对空间利用率和查询性能的一种折中方案。

10.并发向HashMap插入数据会存在什么问题?

a)首先我们知道HashMap是线程不安全的容器。线程不安全体现在哪?体现在对其底层的数据table[]的更新没有加锁。

b)向HashMap中并发地put新的数据时在容器扩容之后将旧的table上的数据转移至新的table上时可能会形成环形链表,从而使查询时造成死循环,导致CPU使用率飙升、系统卡死等。

c)假设线程T1向HashMap中put数据并且当put的数据将要存在某个长度大于1的链表中并且这时刚好需要扩容并且恰巧这时又来了另一个线程T2,在把原来talbe[]上的数据rehash到新的table上时, 可能会出现T1创建的newTable 的Entry的next指向了T2创建的newTable的Entry上。从而使该Entry所在的链表形成环形链表,这样在调用get(key)查询数据时就会造成死循环,从而使CPU飙升。

可参考:https://www.jianshu.com/p/1e9cf0ac07f4

11.java8中的HashMap相对java7有何变化?

a)java8中HashMap引入了红黑树,向HashMap中put数据时如果链表长度>=8则由原来的继续使用链表存储改为使用红黑树存储。这使得hashcode书写不当导致链表较长时HashMap的查询性能得到了提升。

b)java8中由原来的向链表头部追加数据改为向链表尾部追加数据。

c)java8中table由原来的Entry<K,V>[]变为了Node<K,V>[]

12.LinkedHashMap与HahMap是什么关系,有什么区别,HashSet与HashMap是什么关系?

a)LinkedHashMap继承自HahMap,LinkedHashMap是有序的,HahMap是无序的。LinkedHashMap的构造函数中新加了参数accessOrder用以设置是按访问顺序排序还是按插入顺序(默认按插入顺序排序)。然后其Entry的实现类中重写了HashMap的Entry的recordAccess方法,并且维护了一个链表用于记录其插入顺序或访问顺序。

b)HashSet是对HashMap的一种包装,其内部采用了HashMap来存储数据,只不过存入HashMap中的value是全部相同的一个Object实例。

13.如何提升HashMap的性能?

合理重写key的hashcode和equals方法或者使用已经重写了hashcode方法和equals方法的类作为key;

合理设置初始容量;

升级JDK版本至JDK8以上(可参考http://blog.jobbole.com/66078/);

14.HashMap有哪些特征?适用场景是?相较于List如何?

a)要了解HashMap的适用场景,需要先了解HashMap的特性。首先Map可以存储多个键值对,维护多个键值对的映射关系。

b)其次Map中的key不可以重复,如果key重复则后来的value后覆盖已有的key对应的value。

c)然后HashMap在遍历时是无序的。

d)然后HashMap是线程不安全的。

e)假设HashMap没有哈希冲突的情况下,HashMap的查询时间复杂度是O(1)。

f)如果业务需要与以上特征符合则可使用HashMap。

g)另外,List也可以达到与HashMap类似的键值对映射,例如List<Date> , 可以把Date的time看作key, 把Date看作value, 这样也能达到映射效果,但是不推荐这种做法。因为这种映射方式在数据量不大时与HashMap的查询速度相差不大,甚至List更快,但是当数据量比较大时HashMap的哈希算法的优势就体现出来了。所以List更适合通过索引去精确查询,而HashMap适用于key-value的映射查询。

参考文章:

https://www.ibm.com/developerworks/cn/java/j-lo-hash/index.html

http://javaconceptoftheday.com/how-hashmap-works-internally-in-java/

https://www.jianshu.com/p/1e9cf0ac07f4

如果觉得有收获记得关注哦。


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

75道阿里Java面试题,你能答上几道?

75道batj高级java面试题

75 道 高级 Java 面试题,你能答上几道?

一份非常值得一看的Java面试题

10道腾讯的Java面试题

Java集合---面试题