致第的第一次(讀源碼):
突然發(fā)現(xiàn)疾忍,我不再年輕了乔外,已經(jīng)到了不得不改變了年紀(jì),自己生命的寬度不夠一罩,深度也不夠杨幼,再不非凡,就一直平庸。法國蒙田說過差购,靈魂沒了目標(biāo)四瘫,就會喪失自己。下了5年欲逃,改變自己找蜜,跳出自己的舒適區(qū)。在人生的賽道上稳析,和自己比賽洗做,超越原來的自己,成為另一個自己的巔峰彰居。
1.數(shù)據(jù)結(jié)構(gòu)
HashMap的底層結(jié)構(gòu):數(shù)組+鏈接诚纸。
核心元素結(jié)構(gòu):
2.put操作
1.如果數(shù)組是空,則填充陈惰。
2.如果key是null畦徘,存儲空的key。
3.計算key對應(yīng)的tableIndex抬闯。
4.如果已經(jīng)存在相同的key(一樣的hash值并且key相等<==或equal>)井辆,則用新的value覆蓋原來的value,并返回原來的value溶握。
5.將新的Entry添加到當(dāng)前的鏈表中(插入到頭結(jié)點后)杯缺。
3.關(guān)鍵代碼
1.為什么table的size必須是2的次冪?
如上圖所示:需要存入<k1,v1>,<k2,v2>.
hash<k1>=1001
hash<k2>=1000
計算tableIndex的代碼:
static int indexFor(int hash, int length) {
return hash & (length-1);
}
如果tableSize=15,那么兩個key對應(yīng)的hash奈虾,hash & (length-1)執(zhí)行的結(jié)果是一樣的夺谁,所以兩個key就會存到同一個bucket里廉赔,這就出現(xiàn)key的沖突肉微。
如果tableSize=16,那么兩個key對應(yīng)的hash,hash & (length-1)執(zhí)行的結(jié)果是不同的蜡塌,避免了key沖突碉纳。
如果沖突嚴(yán)重,那么大部分的Entry就會落在同一個bucket中馏艾,那么在執(zhí)行g(shù)et操作的時候劳曹,遍歷鏈表就會耗時。
4.get操作
5.resize
當(dāng)hashmap中的元素越來越多的時候琅摩,碰撞的幾率也就越來越高(因為數(shù)組的長度是固定的)铁孵,所以為了提高查詢的效率,就要對hashmap的數(shù)組進(jìn)行擴(kuò)容房资,在hashmap數(shù)組擴(kuò)容之后蜕劝,最消耗性能的點就出現(xiàn)了:原數(shù)組中的數(shù)據(jù)必須重新計算其在新數(shù)組中的位置,并放進(jìn)去,這就是resize岖沛。
HashMap擴(kuò)容的場景:元素個數(shù) > 數(shù)組大小*loadFactor暑始。
盡量避免HashMap的resize,因為這個操作比較耗時婴削。那么在初始化HashMap的時候廊镜,如果避免執(zhí)行resize,同時又滿足“&”的要求呢唉俗?
舉個栗子:
有1000個元素new HashMap(1000), 但是理論上來講new HashMap(1024)更合適嗤朴,更何況,hashmap也自動會將其設(shè)置為1024虫溜。 但是new HashMap(1024)還不是更合適的播赁,因為0.75*1024 < 1000, 也就是說為了讓0.75 * size > 1000, 我們必須這樣new HashMap(2048)才最合適,既考慮了&的問題吼渡,也避免了resize的問題容为。
6.為什么自定義的key,必須重寫hashcode和equals方法呢寺酪?
比如說自定義了一個User坎背,以User作為key,存入HashMap寄雀。
定義
User u1 = new User(1,"dcx");
User u2 = new User(1,"dcx");
雖然u1和u2具有共同的屬性得滤,但是u1和u2的hashcode是不一樣的(如果不重寫,hashcode默認(rèn)是對象的內(nèi)存地址)盒犹。
hashcode不一樣懂更,tableIndex = hashcode & (tableLength - 1)也不一樣。這樣在get的時候急膀,永遠(yuǎn)也獲取不到了沮协。
如果重寫了hashcode,但是沒有重寫equals方法卓嫂,那么tableIndex一樣慷暂,但是在遍歷的時候,是需要equals來做判斷的晨雳。因此必須重寫equals方法行瑞。
第一次寫文章,好LOW啊餐禁。不過堅持>激情血久,堅持為王!加油帮非!