HashMap面试常问问题

最近在准备秋招面试,集合知识的储备在面试过程中必不可少,由于jdk1.8中红黑树的加入以及concurrentHashMap的出现,由HashMap进入,可以问关于线程高并发的安全问题引入到并发锁的对比,或者可以由数组,链表到达红黑树引入数据结构的问题。可见HashMap的基础 直接决定了会不会有下面问题的扩展,掌握这个势在必得。
很多人懂这个的原理,但是心中的理解表达不出来,这样子在面试中真的很亏。
下面总结了常见的HashMap的面试问题,下面直接附加有答案,感觉自己没有把握的可以直接背过。

一、什么是HashMap?

HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干,数组每一个元素的初始值都是Null。这些就是HashMap的定义了。
点到为止,有的面试官不喜欢说的过多。如果想要输的多一点,,可以参考文末的总结

二、你为什么用到它?

HashMap可以接受null键值和值,而Hashtable则不能;
HashMap是非synchronized,所以相对而说,HashMap很快;
以及HashMap储存的是键值对,以一种数据之间的对应关系。

三、你知道HashMap的工作原理吗?
这时候慢慢的进入集合的问题状态了,准备好面对10分钟左右吧

HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。”这里关键点在于指出,HashMap是在bucket中储存键对象和值对象,作为Map.Entry。

四、你知道HashMap的get()方法的工作原理吗?

首先根据对象的Hash值进行数组方面的寻找,然后找到这个数组之后,判断key是不是唯一,如果key唯一,则直接返回,如果不唯一,则使用equals进行值的判断,最后返回数据。

“五、当两个对象的hashcode相同会发生什么?”
这个问题基本上就是分界点了

一些面试者会回答因为hashcode相同,所以两个对象是相等的,HashMap将会抛出异常,或者不会存储它们。
如果之前的问题回答的好,面试官的印象比较好,可能会提醒他们有equals()和hashCode()两个方法,并告诉他们两个对象就算hashcode相同,但是它们可能并不相等。
如果掌握的不太好,一些面试者可能就此放弃。那下面的问题也就不了了之了,等于放弃了一个很好的机会。
而这个问题的答案是:因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。这个时候要理解根据hashcode来划分的数组,如果数组的坐标相同,则进入链表这个数据结构中了,一般的添加都在最前面,也就是和数组下标直接相连的地方,链表长度到达8的时候,jdk1.8上升为红黑树,这样说,无疑是直接的加分项。有的面试官直接跳入数据结构,有的会直接继续挖掘。

六、如果两个键的hashcode相同,你如何获取值对象?

当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后获取值对象,如果有两个值对象储存在同一个bucket,将会遍历链表直到找到值对象。找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。【完美的答案!】

请注意:
许多情况下,面试者会在这个环节中出错,因为他们混淆了hashCode()和equals()方法。因为在此之前hashCode()屡屡出现,而equals()方法仅仅在获取值对象的时候才出现。一些优秀的开发者会指出使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生,提高效率。不可变性使得能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择。

七、如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
【问到这个问题之后,要及时的意识到面试官要把你往线程安全的方向引入了,做好准备。】

默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

八、你了解重新调整HashMap大小存在什么问题吗?

你可能回答不上来,这时面试官会提醒你当多线程的情况下,可能产生条件竞争(race condition)。
当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。
(如果线程方面的知识储备还不错,那这个时候,你可以质问面试官,为什么这么奇怪,要在多线程的环境下使用HashMap呢?,不直接使用concurrentHashMap)

然后,恭喜你,你的线程面试开始啦!!!


如果读者对这个问题感兴趣,可以再来看看这些问题设计哪些知识点:
● hashing的概念
● HashMap中解决碰撞的方法
● equals()和hashCode()的应用,以及它们在HashMap中的重要性
● 不可变对象的好处
● HashMap多线程的条件竞争
● 重新调整HashMap的大小


总结
HashMap的工作原理
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。

猜你喜欢

转载自blog.csdn.net/weixin_42636552/article/details/82016183