更新時(shí)間:2021-07-12 16:49:01 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽1015次
散列法(Hashing)是一種將字符組成的字符串轉(zhuǎn)換為固定長(zhǎng)度(一般是更短長(zhǎng)度)的數(shù)值或索引值的方法,稱為散列法,也叫哈希法。由于通過(guò)更短的哈希值比用原始值進(jìn)行數(shù)據(jù)庫(kù)搜索更快,這種方法一般用來(lái)在數(shù)據(jù)庫(kù)中建立索引并進(jìn)行搜索,同時(shí)還用在各種解密算法中。
對(duì)比:Hashtable、HashMap、TreeMap
Hashtable是早期Java類庫(kù)提供的一個(gè)哈希表實(shí)現(xiàn),本身是同步的,不支持null鍵和值,由于同步導(dǎo)致的性能開(kāi)銷,所以已經(jīng)很少被推薦使用。
HashMap與HashTable主要區(qū)別在于HashMap不是同步的,支持null鍵和值等。通常情況下,HashMap進(jìn)行put或者get操作,可以達(dá)到常數(shù)時(shí)間的性能,所以它是絕大部分利用鍵值對(duì)存取場(chǎng)景的首選。
TreeMap則是基于紅黑樹(shù)的一種提供順序訪問(wèn)的Map,和HashMap不同,它的get、put、remove之類操作都是O(log(n))的時(shí)間復(fù)雜度,具體順序可以由指定的Comparator來(lái)決定,或者根據(jù)鍵的自然順序來(lái)判斷。
HashMap是基于哈希表的Map接口的非同步實(shí)現(xiàn)。此實(shí)現(xiàn)提供所有可選的映射操作,并允許使用null值和null鍵。HashMap儲(chǔ)存的是鍵值對(duì),HashMap很快。此類不保證映射的順序,特別是它不保證該順序恒久不變。
HashMap內(nèi)部結(jié)構(gòu):可以看作是數(shù)組和鏈表結(jié)合組成的復(fù)合結(jié)構(gòu),數(shù)組被分為一個(gè)個(gè)桶(bucket),每個(gè)桶存儲(chǔ)有一個(gè)或多個(gè)Entry對(duì)象,每個(gè)Entry對(duì)象包含三部分key(鍵)、value(值),next(指向下一個(gè)Entry),通過(guò)哈希值決定了Entry對(duì)象在這個(gè)數(shù)組的尋址;哈希值相同的Entry對(duì)象(鍵值對(duì)),則以鏈表形式存儲(chǔ)。如果鏈表大小超過(guò)樹(shù)形轉(zhuǎn)換的閾值(TREEIFY_THRESHOLD=8),鏈表就會(huì)被改造為樹(shù)形結(jié)構(gòu)。
查詢時(shí)間復(fù)雜度:HashMap的本質(zhì)可以認(rèn)為是一個(gè)數(shù)組,數(shù)組的每個(gè)索引被稱為桶,每個(gè)桶里放著一個(gè)單鏈表,一個(gè)節(jié)點(diǎn)連著一個(gè)節(jié)點(diǎn)。很明顯通過(guò)下標(biāo)來(lái)檢索數(shù)組元素時(shí)間復(fù)雜度為O(1),而且遍歷鏈表的時(shí)間復(fù)雜度是O(n),所以在鏈表長(zhǎng)度盡可能短的前提下,HashMap的查詢復(fù)雜度接近O(1)
數(shù)組:存儲(chǔ)區(qū)間連續(xù),占用內(nèi)存嚴(yán)重,尋址容易,插入刪除困難;
鏈表:存儲(chǔ)區(qū)間離散,占用內(nèi)存比較寬松,尋址困難,插入刪除容易;
Hashmap綜合應(yīng)用了這兩種數(shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)了尋址容易,插入刪除也容易。
HashMap的工作原理:HashMap是基于散列法(又稱哈希法)的原理,使用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ì)象。HashMap是在bucket中儲(chǔ)存鍵對(duì)象和值對(duì)象,作為Map.Entry。并不是僅僅只在bucket中存儲(chǔ)值。
put存值的方法,過(guò)程如下:
“如果HashMap的大小超過(guò)了負(fù)載因子(load factor)定義的容量,怎么辦?”
HashMap的擴(kuò)容閾值(threshold=capacity*loadFactor容量范圍是16~2的30次方),就是通過(guò)它和size進(jìn)行比較來(lái)判斷是否需要擴(kuò)容。默認(rèn)的負(fù)載因子大小為0.75,也就是說(shuō),當(dāng)一個(gè)map填滿了75%的bucket時(shí)候,將會(huì)創(chuàng)建原來(lái)HashMap大小的兩倍的bucket數(shù)組(jdk1.6,但不超過(guò)最大容量),來(lái)重新調(diào)整map的大小,并將原來(lái)的對(duì)象放入新的bucket數(shù)組中。這個(gè)過(guò)程叫作rehashing,因?yàn)樗{(diào)用hash方法找到新的bucket位置。
針對(duì)哈希表直接定址可能存在hash沖突,舉一個(gè)簡(jiǎn)單的例子,例如:
這樣我們發(fā)現(xiàn)index=0的地方事實(shí)上存取了A,B,C三個(gè)鍵值對(duì),它們通過(guò)next這個(gè)屬性鏈接在一起。對(duì)于不同的元素,可能計(jì)算出了相同的函數(shù)值,這樣就產(chǎn)生了hash沖突,那要解決沖突,又有哪些方法呢?具體如下:
HashMap就是使用鏈地址法來(lái)解決沖突的(jdk8中采用平衡樹(shù)來(lái)替代鏈表存儲(chǔ)沖突的元素,但hash()方法原理相同)。當(dāng)兩個(gè)對(duì)象的hashcode相同時(shí),它們的bucket位置相同,碰撞就會(huì)發(fā)生。此時(shí),可以將put進(jìn)來(lái)的K-V對(duì)象插入到鏈表的尾部。對(duì)于儲(chǔ)存在同一個(gè)bucket位置的鏈表對(duì)象,可通過(guò)鍵對(duì)象的equals()方法用來(lái)找到鍵值對(duì)。
以上就是動(dòng)力節(jié)點(diǎn)小編介紹的"HashMap原理的深入了解",希望對(duì)大家有幫助,想了解更多可查看Java基礎(chǔ)教程,如有疑問(wèn),請(qǐng)?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為您服務(wù)。
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
有基礎(chǔ) 直達(dá)就業(yè)
業(yè)余時(shí)間 高薪轉(zhuǎn)行
工作1~3年,加薪神器
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)