Java中HashMap的工作原理:
一院促,存儲方式: Java中的HashMap是以鍵值對(key-value)的形式存儲元素的吨拍。
二防症,調(diào)用原理: HashMap需要一個hash函數(shù)溅漾,它使用hashCode()和equals()方法來向集合/從集合添加和檢索元素。當調(diào)用put()方法的時候计露,HashMap會計算key的hash值博脑,然后把鍵值對存儲在集合中合適的索引上。如果key已經(jīng)存在了票罐,value會被更新成新值叉趣。
三,其他熱性: HashMap的一些重要的特性是它的容量(capacity)该押,負載因子(load factor)和擴容極限(threshold resizing)疗杉。
一、Java中的hashCode和equals:
1蚕礼、關于hashCode
hashCode的存在主要是用于查找的快捷性烟具,如Hashtable,HashMap等奠蹬,hashCode是用來在散列存儲結(jié)構(gòu)中確定對象的存儲地址的
如果兩個對象相同朝聋,就是適用于equals(java.lang.Object) 方法,那么這兩個對象的hashCode一定要相同
如果對象的equals方法被重寫罩润,那么對象的hashCode也盡量重寫,并且產(chǎn)生hashCode使用的對象翼馆,一定要和equals方法中使用的一致割以,否則就會違反上面提到的第2點
兩個對象的hashCode相同金度,并不一定表示兩個對象就相同,也就是不一定適用于equals(java.lang.Object) 方法严沥,只能夠說明這兩個對象在散列存儲結(jié)構(gòu)中猜极,如Hashtable,他們“存放在同一個籃子里“
再歸納一下就是hashCode是用于查找使用的消玄,而equals是用于比較兩個對象的是否相等的跟伏。
2、關于equals
1.equals和==
==用于比較引用和比較基本數(shù)據(jù)類型時具有不同的功能:
比較基本數(shù)據(jù)類型翩瓜,如果兩個值相同受扳,則結(jié)果為true
而在比較引用時,如果引用指向內(nèi)存中的同一對象兔跌,結(jié)果為true;
equals()作為方法勘高,實現(xiàn)對象的比較。由于==運算符不允許我們進行覆蓋坟桅,也就是說它限制了我們的表達华望。因此我們復寫equals()方法,達到比較對象內(nèi)容是否相同的目的仅乓。而這些通過==運算符是做不到的赖舟。
2.object類的equals()方法的比較規(guī)則為:如果兩個對象的類型一致,并且內(nèi)容一致夸楣,則返回true,這些類有:
java.io.file,java.util.Date,java.lang.string,包裝類(Integer,Double等)
String s1=new String("abc");
String s2=new String("abc");
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
運行結(jié)果為false true
Java中HashMap的實現(xiàn)原理:
1.??? HashMap概述
??? HashMap是基于哈希表的Map接口的非同步實現(xiàn)宾抓。此實現(xiàn)提供所有可選的映射操作,并允許使用null值和null鍵裕偿。此類不保證映射的順序洞慎,特別是它不保證該順序恒久不變。
??? 在java編程語言中嘿棘,最基本的結(jié)構(gòu)就是兩種劲腿,一個是數(shù)組,另外一個是模擬指針(引用)鸟妙,所有的數(shù)據(jù)結(jié)構(gòu)都可以用這兩個基本結(jié)構(gòu)來構(gòu)造的焦人,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數(shù)據(jù)結(jié)構(gòu)重父,即數(shù)組和鏈表的結(jié)合體花椭。
圖中可以看出以上圖片HashMap底層就是一個數(shù)組結(jié)構(gòu),數(shù)組中的每一項又是一個鏈表房午。當新建一個HashMap的時候矿辽,就會初始化一個數(shù)組。
Java源碼:
可以看出,Entry就是數(shù)組中的元素袋倔,每個 Map.Entry 其實就是一個key-value對雕蔽,它持有一個指向下一個元素的引用,這就構(gòu)成了鏈表宾娜。
2批狐、HashMap實現(xiàn)存儲和讀取:
(1)存儲:
根據(jù)hash值得到這個元素在數(shù)組中的位置 即下標前塔,如果數(shù)組該位置上已經(jīng)存放有其他元素了嚣艇,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭华弓,最先加入的放在鏈尾食零。如果數(shù)組該位置上沒有元素,就直接將該元素放到此數(shù)組中的該位置上该抒。
(2)讀然藕椤:
從HashMap中get元素時,首先計算key的hashCode凑保,找到數(shù)組中對應位置的某一元素冈爹,然后通過key的equals方法在對應位置的鏈表中找到需要的元素。
(3)總結(jié):
HashMap 在底層將 key-value 當成一個整體進行處理欧引,這個整體就是一個 Entry 對象频伤。HashMap 底層采用一個 Entry[ ] 數(shù)組來保存所有的 key-value 對,當需要存儲一個 Entry 對象時芝此,會根據(jù)hash算法來決定其在數(shù)組中的存儲位置憋肖,在根據(jù)equals方法決定其在該數(shù)組位置上的鏈表中的存儲位置;當需要取出一個Entry時婚苹,也會根據(jù)hash算法找到其在數(shù)組中的存儲位置岸更,再根據(jù)equals方法從該位置上的鏈表中取出該Entry。
3膊升、HashMap的resize:
?當hashmap中的元素越來越多的時候怎炊,碰撞的幾率也就越來越高? 因為數(shù)組的長度是固定的,所以為了提高查詢的效率廓译,就要對hashmap的數(shù)組進行擴容评肆,數(shù)組擴容這個操作也會出現(xiàn)在ArrayList中,所以這是一個通用的操作非区,很多人對它的性能表示過懷疑瓜挽,不過想想我們的“均攤”原理,就釋然了征绸,而在hashmap數(shù)組擴容之后久橙,最消耗性能的點就出現(xiàn)了:原數(shù)組中的數(shù)據(jù)必須重新計算其在新數(shù)組中的位置俄占,并放進去,這就是resize淆衷。
?????? 那么hashmap什么時候進行擴容呢颠放?當hashmap中的元素個數(shù)超過數(shù)組大小*loadFactor時,就會進行數(shù)組擴容吭敢,loadFactor的默認值為0.75,也就是說暮芭,默認情況下鹿驼,數(shù)組大小為16,那么當hashmap中元素個數(shù)超過16*0.75=12的時候辕宏,就把數(shù)組的大小擴展為2*16=32畜晰,即擴大一倍,然后重新計算每個元素在數(shù)組中的位置瑞筐,而這是一個非常消耗性能的操作凄鼻,所以如果我們已經(jīng)預知hashmap中元素的個數(shù),那么預設元素的個數(shù)能夠有效的提高hashmap的性能聚假。比如說块蚌,我們有1000個元素new HashMap(1000), 但是理論上來講new HashMap(1024)更合適,不過上面annegu已經(jīng)說過膘格,即使是1000峭范,hashmap也自動會將其設置為1024。 但是new HashMap(1024)還不是更合適的瘪贱,因為0.75*1000 < 1000, 也就是說為了讓0.75 * size > 1000, 我們必須這樣new HashMap(2048)才最合適纱控,既考慮了&的問題,也避免了resize的問題菜秦。
總結(jié):HashMap的實現(xiàn)原理:
利用key的hashCode重新hash計算出當前對象的元素在數(shù)組中的下標
存儲時甜害,如果出現(xiàn)hash值相同的key,此時有兩種情況球昨。(1)如果key相同尔店,則覆蓋原始值;(2)如果key不同(出現(xiàn)沖突)褪尝,則將當前的key-value放入鏈表中
獲取時闹获,直接找到hash值對應的下標,在進一步判斷key是否相同河哑,從而找到對應值避诽。
理解了以上過程就不難明白HashMap是如何解決hash沖突的問題,核心就是使用了數(shù)組的存儲方式璃谨,然后將沖突的key的對象放入鏈表中沙庐,一旦發(fā)現(xiàn)沖突就在鏈表中做進一步的對比鲤妥。