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