1、“你知道HashMap的工作原理嗎?” “你知道HashMap的get()方法的工作原理嗎?”
HashMap是基于hashing的原理,我們使用put(key, value)存儲(chǔ)對(duì)象到HashMap中,使用get(key)從HashMap中獲取對(duì)象。當(dāng)我們給put()方法傳遞鍵和值時(shí),我們先對(duì)鍵調(diào)用hashCode()方法,返回的hashCode用于找到bucket位置來(lái)儲(chǔ)存Entry對(duì)象?!边@里關(guān)鍵點(diǎn)在于指出,HashMap是在bucket中儲(chǔ)存鍵對(duì)象和值對(duì)象,作為Map.Entry。
2、“當(dāng)兩個(gè)對(duì)象的hashcode相同會(huì)發(fā)生什么?”
因?yàn)閔ashcode相同,所以它們的bucket位置相同,‘碰撞’會(huì)發(fā)生。因?yàn)镠ashMap使用LinkedList存儲(chǔ)對(duì)象,這個(gè)Entry(包含有鍵值對(duì)的Map.Entry對(duì)象)會(huì)存儲(chǔ)在LinkedList中。(當(dāng)向 HashMap 中添加 key-value 對(duì),由其 key 的 hashCode() 返回值決定該 key-value 對(duì)(就是 Entry 對(duì)象)的存儲(chǔ)位置。當(dāng)兩個(gè) Entry 對(duì)象的 key 的 hashCode() 返回值相同時(shí),將由 key 通過(guò) eqauls() 比較值決定是采用覆蓋行為(返回 true),還是產(chǎn)生 Entry 鏈(返回 false)。)
3、“如果兩個(gè)鍵的hashcode相同,你如何獲取值對(duì)象?”
當(dāng)我們調(diào)用get()方法,HashMap會(huì)使用鍵對(duì)象的hashcode找到bucket位置,然后獲取值對(duì)象。如果有兩個(gè)值對(duì)象儲(chǔ)存在同一個(gè)bucket,將會(huì)遍歷LinkedList直到找到值對(duì)象。找到bucket位置之后,會(huì)調(diào)用keys.equals()方法去找到LinkedList中正確的節(jié)點(diǎn),最終找到要找的值對(duì)象。(當(dāng)程序通過(guò) key 取出對(duì)應(yīng) value 時(shí),系統(tǒng)只要先計(jì)算出該 key 的 hashCode() 返回值,在根據(jù)該 hashCode 返回值找出該 key 在 table 數(shù)組中的索引,然后取出該索引處的 Entry,最后返回該 key 對(duì)應(yīng)的 value 即可。)
4、“如果HashMap的大小超過(guò)了負(fù)載因子(load factor)定義的容量,怎么辦?”
當(dāng)一個(gè)map填滿了75%的bucket時(shí)候,和其它集合類(如ArrayList等)一樣,將會(huì)創(chuàng)建原來(lái)HashMap大小的兩倍的bucket數(shù)組,來(lái)重新調(diào)整map的大小,并將原來(lái)的對(duì)象放入新的bucket數(shù)組中。這個(gè)過(guò)程叫作rehashing,因?yàn)樗{(diào)用hash方法找到新的bucket位置。
5、“你了解重新調(diào)整HashMap大小存在什么問(wèn)題嗎?”
當(dāng)重新調(diào)整HashMap大小的時(shí)候,確實(shí)存在條件競(jìng)爭(zhēng),因?yàn)槿绻麅蓚€(gè)線程都發(fā)現(xiàn)HashMap需要重新調(diào)整大小了,它們會(huì)同時(shí)試著調(diào)整大小。在調(diào)整大小的過(guò)程中,存儲(chǔ)在LinkedList中的元素的次序會(huì)反過(guò)來(lái),因?yàn)橐苿?dòng)到新的bucket位置的時(shí)候,HashMap并不會(huì)將元素放在LinkedList的尾部,而是放在頭部,這是為了避免尾部遍歷(tail traversing)。如果條件競(jìng)爭(zhēng)發(fā)生了,那么就死循環(huán)了。這個(gè)時(shí)候,你可以質(zhì)問(wèn)面試官,為什么這么奇怪,要在多線程的環(huán)境下使用HashMap呢?