赞
踩
最近在准备秋招面试,集合知识的储备在面试过程中必不可少,虽然现在已经到了jdk11,但是好多公司暂时还是jdk.1.8,并且jdk.1.8的新特性,在HashMap这里是一个大突破
jdk1.8中红黑树的加入
jdk1.7变为链表的头插法以及jdk1.8的尾插法区别
所以由HashMap进入,可以问关于线程高并发的安全问题引入到并发锁的对比,或者可以由数组,链表到达红黑树引入数据结构的问题。可见HashMap的基础 直接决定了会不会有下面问题的扩展,掌握这个势在必得。
很多人懂这个的原理,但是心中的理解表达不出来,这样子在面试中真的很亏。
下面总结了常见的HashMap的面试问题,下面直接附加有答案,感觉自己没有把握的可以直接背过。
HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干,数组每一个元素的初始值都是Null。这些就是HashMap的定义了。
点到为止,有的面试官不喜欢说的过多。如果想要输的多一点,,可以参考文末的总结
HashMap可以接受null键值和值,而Hashtable则不能;
HashMap是非synchronized,所以相对而说,HashMap很快;
以及HashMap储存的是键值对,以一种数据之间的对应关系。
这时候慢慢的进入集合的问题状态了,准备好面对10分钟左右吧
HashMap是基于hashing的原理,我们使用put(key,
value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。”这里关键点在于指出,HashMap是在bucket中储存键对象和值对象,作为Map.Entry。
首先根据对象的Hash值进行数组方面的寻找,然后找到这个数组之后,判断key是不是唯一,如果key唯一,则直接返回,如果不唯一,则使用equals进行值的判断,最后返回数据。
【这个问题基本上就是分界点了】
一些面试者会回答因为hashcode相同,所以两个对象是相等的,HashMap将会抛出异常,或者不会存储它们。
如果之前的问题回答的好,面试官的印象比较好,可能会提醒他们有equals()和hashCode()两个方法,并告诉他们两个对象就算hashcode相同,但是它们可能并不相等。
如果掌握的不太好,一些面试者可能就此放弃。那下面的问题也就不了了之了,等于放弃了一个很好的机会。
而这个问题的答案是:因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。这个时候要理解根据hashcode来划分的数组,
如果数组的坐标相同,则进入链表这个数据结构中了,jdk1.7及以前为头插法,jdk1.8之后是尾插法,在jdk1.8之后,当链表长度到达8的时候,jdk1.8上升为红黑树,
这样说,无疑是直接的加分项。有的面试官直接跳入数据结构,有的会直接继续挖掘。
当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后获取值对象,如果有两个值对象储存在同一个bucket,将会遍历链表直到找到值对象。找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。
【完美的答案!】
请注意:
许多情况下,面试者会在这个环节中出错,因为他们混淆了hashCode()和equals()方法。
因为在此之前hashCode()屡屡出现,而equals()方法仅仅在获取值对象的时候才出现。
一些优秀的开发者会指出使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生,提高效率。
不可变性使得能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择。
【问到这个问题之后,要及时的意识到面试官要把你往线程安全的方向引入了,做好准备。】
默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。
你可能回答不上来,这时面试官会提醒你当多线程的情况下,可能产生条件竞争(race condition)。
当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing),原数组[j]位置上的桶移到了新数组[j+原数组长度]。如果条件竞争发生了,那么就死循环了。
(如果线程方面的知识储备还不错,那这个时候,你可以质问面试官,为什么这么奇怪,要在多线程的环境下使用HashMap呢?,不直接使用concurrentHashMap)
然后,恭喜你,你的线程面试开始啦!!!
如果读者对这个问题感兴趣,可以再来看看这些问题设计哪些知识点:
● hashing的概念
● HashMap中解决碰撞的方法
● equals()和hashCode()的应用,以及它们在HashMap中的重要性
● 不可变对象的好处
● HashMap多线程的条件竞争
● 重新调整HashMap的大小
HashMap的工作原理
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。