摘要:放入第二個(gè)元素,再計(jì)算哈希值,發(fā)現(xiàn)與第一個(gè)節(jié)點(diǎn)的哈希值相同。覆蓋又來(lái)一個(gè),哈希值相等,長(zhǎng)度為,與是相等的,直接將原節(jié)點(diǎn)的值覆蓋。第五個(gè),哈希值相等,長(zhǎng)度為,與相等,覆蓋,由修改為。
問(wèn)題描述
一道來(lái)自Java官方twitter的問(wèn)題。風(fēng)格很像目前國(guó)內(nèi)各大互聯(lián)網(wǎng)公司的筆試題。
public class MapEqualsChallenge { public static void main(String[] args) { Mapmap = new LinkedHashMap<>(); map.put(new Stark("Arya"), "1"); map.put(new Stark("Ned"), "2"); map.put(new Stark("Sansa"), "3"); map.put(new Stark("Bran"), "4"); map.put(new Stark("Jaime"), "5"); map.forEach((key, value) -> System.out.println(value)); } static class Stark { String name; public Stark(String name) { this.name = name; } @Override public boolean equals(Object obj) { return ((Stark) obj).name.length() == this.name.length(); } @Override public int hashCode() { return 4000 << 2 * 2000 / 10000; } } }
挺有意義的一道題,絕對(duì)是學(xué)習(xí)equals和hashcode的經(jīng)典案例,為官方社區(qū)點(diǎn)個(gè)贊。
分析 Java 集合這是一個(gè)有關(guān)Java集合的關(guān)系圖,最初覺(jué)得這個(gè)好復(fù)雜,但是現(xiàn)在再去看看,其實(shí)也很簡(jiǎn)單。
Collection和Map接口都是我們常用的,這個(gè)圖有一點(diǎn)問(wèn)題,在JDK的源碼中,Map和Collection毫無(wú)關(guān)系。
List:強(qiáng)調(diào)數(shù)據(jù)在集合中的位置。
Set:強(qiáng)調(diào)集合中元素不允許重復(fù)。
Map:強(qiáng)調(diào)集合中的元素根據(jù)key來(lái)查詢。
如果你不明白三者的區(qū)別與聯(lián)系,請(qǐng)看《Head First Java 第二版》557頁(yè),書(shū)中用圖示對(duì)三者進(jìn)行了詳細(xì)的闡述。
LinkedHashMap之前研究過(guò)HashMap和ConcurrentHashMap(線程安全的HashMap),這個(gè)LinkedHashMap倒是第一次見(jiàn)。
LinkedHashMap繼承自HashMap,二者區(qū)別不大。
存放數(shù)據(jù),根據(jù)key計(jì)算哈希值,然后根據(jù)哈希值計(jì)算該元素應(yīng)當(dāng)存儲(chǔ)在數(shù)組中的位置,如果hash沖突,并且不是一個(gè)對(duì)象,則用鏈表處理。
HashMap實(shí)現(xiàn)的是單向鏈表,LinkedHashMap實(shí)現(xiàn)的是雙向鏈表。
hashCode這是源碼中計(jì)算哈希值的方法,先取對(duì)象的哈希值,然后再進(jìn)行相應(yīng)的處理。
所以再去看put過(guò)程:key是一個(gè)對(duì)象,然后LinkedHashMap會(huì)調(diào)用key的hashCode方法。
map.put(new Stark("Arya"), "1");
我們可能一看這個(gè)hashCode方法就嚇蒙了,這怎么算?。科鋵?shí)我們完全沒(méi)必要去計(jì)算這個(gè)表達(dá)式,這是個(gè)常量表達(dá)式,所以不管new出來(lái)多少個(gè)對(duì)象,哈希值都是一樣的。再去調(diào)用LinkedHashMap的hash方法,返回值也是一樣。
@Override public int hashCode() { return 4000 << 2 * 2000 / 10000; }
先不去管到底是雙向鏈表還是單向鏈表,反正哈希值相同,這些個(gè)鍵值對(duì)肯定在一個(gè)鏈表上。
map.put(new Stark("Arya"), "1");
執(zhí)行第一行代碼,插入key為Arya對(duì)象,value為1的節(jié)點(diǎn)對(duì)象。
equalsmap.put(new Stark("Ned"), "2");
放入第二個(gè)元素,再計(jì)算哈希值,發(fā)現(xiàn)與第一個(gè)節(jié)點(diǎn)key的哈希值相同。
如果兩個(gè)對(duì)象的哈希值相等,則這兩個(gè)對(duì)象有可能相等;如果兩個(gè)對(duì)象的哈希值不相等,那這兩個(gè)對(duì)象肯定不相等。
哈希值相等,然后用equals方法判斷兩個(gè)對(duì)象是否相等。
@Override public boolean equals(Object obj) { return ((Stark) obj).name.length() == this.name.length(); }
重寫(xiě)過(guò)的equals是根據(jù)對(duì)象名字的長(zhǎng)度來(lái)判等的,如果兩個(gè)對(duì)象名字長(zhǎng)度相等,那就認(rèn)為是同一對(duì)象。
Arya與Ned長(zhǎng)度不相等,所以LinkedHashMap認(rèn)為這是兩個(gè)對(duì)象,再去新建一個(gè)Node,指過(guò)去。
注意:這里有一個(gè)插入的位置問(wèn)題,到底是在鏈表頭部插新節(jié)點(diǎn)還是在尾部插新節(jié)點(diǎn)?據(jù)說(shuō)面試官還考過(guò)?
答案是:在Java8之前,是在頭部插入節(jié)點(diǎn);在Java8中,是在尾部插入節(jié)點(diǎn)。
文章鏈接:HashMap到底是插入鏈表頭部還是尾部
map.put(new Stark("Sansa"), "3");
又來(lái)一個(gè),哈希值相等,名字長(zhǎng)度為5,沒(méi)有一樣的key,再插一個(gè)新節(jié)點(diǎn)。
覆蓋map.put(new Stark("Bran"), "4");
又來(lái)一個(gè),哈希值相等,長(zhǎng)度為4,與Arya 1是相等的,直接將原節(jié)點(diǎn)的值覆蓋。由1修改為4。
map.put(new Stark("Jaime"), "5");
第五個(gè),哈希值相等,長(zhǎng)度為5,與Sansa 3相等,覆蓋,由3修改為5。
結(jié)果分析了一大堆,切換到IDE中運(yùn)行,與預(yù)期結(jié)果一致。
總結(jié)學(xué)然后知不足,教然后知困!
之前覺(jué)得自己對(duì)HashMap還是聽(tīng)明白的,但當(dāng)自己去畫(huà)各種示意圖時(shí),發(fā)現(xiàn)還有很多細(xì)節(jié)去需要學(xué)習(xí)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72061.html
摘要:類的源碼對(duì)象的方法其中會(huì)消耗大量時(shí)間。所以,如果在基于哈希表的容器中存儲(chǔ)對(duì)象,簡(jiǎn)直就是災(zāi)難。下面這段代碼,對(duì)比了和在存儲(chǔ)次時(shí)的表現(xiàn)輸出為所以,基于哈希表實(shí)現(xiàn)的容器最好不要用。這也給我們啟發(fā)結(jié)尾的最好還是加上以上,本周末發(fā)現(xiàn)的一些坑。 背景介紹 最近再做一個(gè)RSS閱讀工具給自己用,其中一個(gè)環(huán)節(jié)是從服務(wù)器端獲取一個(gè)包含了RSS源列表的json文件,再根據(jù)這個(gè)json文件下載、解析RSS內(nèi)容...
摘要:我們使用調(diào)試器卻發(fā)現(xiàn)在中已經(jīng)存儲(chǔ)了這個(gè)對(duì)象。中和有一個(gè)契約如果兩個(gè)對(duì)象相等的話,它們的必須相等但如果兩個(gè)對(duì)象的相等的話,這兩個(gè)對(duì)象不一定相等。的結(jié)構(gòu)能夠快速找到一個(gè)對(duì)象,而不是進(jìn)行較慢的線性查找??梢钥醋魇菙?shù)組的數(shù)組。 java.lang.Object類中有兩個(gè)非常重要的方法: public boolean equals(Object obj) public int hashCode...
摘要:所以利用哈希表這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)具體類時(shí),需要設(shè)計(jì)個(gè)好的函數(shù),使沖突盡可能的減少其次是需要解決發(fā)生沖突后如何處理。源碼剖析首先從構(gòu)造函數(shù)開(kāi)始講,遵循集合框架的約束,提供了一個(gè)參數(shù)為空的構(gòu)造函數(shù)與有一個(gè)參數(shù)且參數(shù)類型為的構(gòu)造函數(shù)。 本文章首發(fā)于個(gè)人博客,鑒于sf博客樣式具有賞心悅目的美感,遂發(fā)表于此,供大家學(xué)習(xí)、批評(píng)。本文還在不斷更新中,最新版可移至個(gè)人博客。? 繼上一篇文章Java集合...
摘要:在中對(duì)象是一切對(duì)象都會(huì)自動(dòng)繼承的一個(gè)類,在這個(gè)類中定義的屬性和方法可以說(shuō)是每個(gè)類都必須的。這里有必要說(shuō)說(shuō)這里對(duì)象里面的幾個(gè)方法返回該對(duì)象的哈希碼值。這些基于表的集合,只能要求被存放的對(duì)象實(shí)現(xiàn)自己的方法,保證的均勻性。 Object 在Java中Object對(duì)象是一切對(duì)象都會(huì)自動(dòng)繼承的一個(gè)類,在這個(gè)類中定義的屬性和方法可以說(shuō)是每個(gè)類都必須的。 這里有必要說(shuō)說(shuō)這里對(duì)象里面的幾個(gè)方法 has...
摘要:繼承的類,泛型為時(shí),注意和其他的類型不同。因?yàn)槭蔷€程安全簡(jiǎn)單來(lái)說(shuō),是個(gè)一維數(shù)組。同樣,指定和,如果中間發(fā)生變化則會(huì)拋出異常。最后,可以,然后,使用基類可以實(shí)現(xiàn)和的快速賦值。線程安全也是線程安全的,和一樣,連函數(shù)都喪心病狂地同步了。 這么幾個(gè)比較常用的但是比較容易混淆的概念同出于 java.util 包。本文僅作幾個(gè)類的淺度解析。 (本文基于JDK1.7,源碼來(lái)自openjdk1.7。)...
閱讀 3404·2022-01-04 14:20
閱讀 3118·2021-09-22 15:08
閱讀 2209·2021-09-03 10:44
閱讀 2324·2019-08-30 15:44
閱讀 1502·2019-08-29 18:40
閱讀 2669·2019-08-29 17:09
閱讀 2996·2019-08-26 13:53
閱讀 3227·2019-08-26 13:37