1.? HashMap與HashTable的區(qū)別?
????????1.HashMap是非線程安全的竟纳,HashTable是線程安全的。
????????2.HashMap的鍵和值都允許有null值存在逊移,而HashTable則不行。
????????3.因?yàn)榫€程安全的問(wèn)題,HashMap效率比HashTable的要高玫膀。
? ? ? ? 4.默認(rèn)容量不同 (HashMap:16? HashTable:11)
2.? HashMap,ConcurrentHashMap與LinkedHashMap的區(qū)別爹脾?
????????ConcurrentHashMap是使用了鎖分段技術(shù)技術(shù)來(lái)保證線程安全的帖旨,鎖分段技術(shù):首先將數(shù)據(jù)分成一段一段的存儲(chǔ),然后給每一段數(shù)據(jù)配一把鎖灵妨,當(dāng)一個(gè)線程占用鎖訪問(wèn)其中一個(gè)段數(shù)據(jù)的時(shí)候解阅,其他段的數(shù)據(jù)也能被其他線程訪問(wèn)
????????ConcurrentHashMap 是在每個(gè)段(segment)中線程安全的
????????LinkedHashMap維護(hù)一個(gè)雙鏈表,可以將里面的數(shù)據(jù)按寫(xiě)入的順序讀出
3. ConcurrentHashMap應(yīng)用場(chǎng)景泌霍?
????????1:ConcurrentHashMap的應(yīng)用場(chǎng)景是高并發(fā)货抄,但是并不能保證線程安全,而同步的HashMap和HashMap的是鎖住整個(gè)容器朱转,而加鎖之后ConcurrentHashMap不需要鎖住整個(gè)容器蟹地,只需要鎖住對(duì)應(yīng)的Segment就好了,所以可以保證高并發(fā)同步訪問(wèn)肋拔,提升了效率锈津。
????????2:可以多線程寫(xiě)。
4.? ConcurrentHashMap把HashMap分成若干個(gè)Segmenet凉蜂?
????????1.get時(shí)琼梆,不加鎖,先定位到segment然后在找到頭結(jié)點(diǎn)進(jìn)行讀取操作窿吩。而value是volatile變量茎杂,所以可以保證在競(jìng)爭(zhēng)條件時(shí)保證讀取最新的值,如果讀到的value是null纫雁,則可能正在修改煌往,那么就調(diào)用ReadValueUnderLock函數(shù),加鎖保證讀到的數(shù)據(jù)是正確的。
????????2.Put時(shí)會(huì)加鎖刽脖,一律添加到hash鏈的頭部羞海。
????????3.Remove時(shí)也會(huì)加鎖,由于next是final類型不可改變曲管,所以必須把刪除的節(jié)點(diǎn)之前的節(jié)點(diǎn)都復(fù)制一遍却邓。
????????4.ConcurrentHashMap允許多個(gè)修改操作并發(fā)進(jìn)行,其關(guān)鍵在于使用了鎖分離技術(shù)院水。它使用了多個(gè)鎖來(lái)控制對(duì)Hash表的不同Segment進(jìn)行的修改腊徙。
????????ConcurrentHashMap能夠保證每一次調(diào)用都是原子操作,但是并不保證多次調(diào)用之間也是原子操作檬某。
5.??Java中Vector和ArrayList的區(qū)別?
? ? ? ?首先看這兩類都實(shí)現(xiàn)List接口撬腾,而List接口一共有三個(gè)實(shí)現(xiàn)類,分別是ArrayList恢恼、Vector和LinkedList民傻。List用于存放多個(gè)元素,能夠維護(hù)元素的次序场斑,并且允許元素的重復(fù)饰潜。3個(gè)具體實(shí)現(xiàn)類的相關(guān)區(qū)別如下:
????????ArrayList是最常用的List實(shí)現(xiàn)類,內(nèi)部是通過(guò)數(shù)組實(shí)現(xiàn)的和簸,它允許對(duì)元素進(jìn)行快速隨機(jī)訪問(wèn)。數(shù)組的缺點(diǎn)是每個(gè)元素之間不能有間隔碟刺,當(dāng)數(shù)組大小不滿足時(shí)需要增加存儲(chǔ)能力锁保,就要講已經(jīng)有數(shù)組的數(shù)據(jù)復(fù)制到新的存儲(chǔ)空間中。當(dāng)從ArrayList的中間位置插入或者刪除元素時(shí)半沽,需要對(duì)數(shù)組進(jìn)行復(fù)制爽柒、移動(dòng)、代價(jià)比較高者填。因此浩村,它適合隨機(jī)查找和遍歷,不適合插入和刪除占哟。
????????Vector與ArrayList一樣心墅,也是通過(guò)數(shù)組實(shí)現(xiàn)的,不同的是它支持線程的同步榨乎,即某一時(shí)刻只有一個(gè)線程能夠?qū)慥ector怎燥,避免多線程同時(shí)寫(xiě)而引起的不一致性,但實(shí)現(xiàn)同步需要很高的花費(fèi)蜜暑,因此铐姚,訪問(wèn)它比訪問(wèn)ArrayList慢。
????????LinkedList是用鏈表結(jié)構(gòu)存儲(chǔ)數(shù)據(jù)的肛捍,很適合數(shù)據(jù)的動(dòng)態(tài)插入和刪除隐绵,隨機(jī)訪問(wèn)和遍歷速度比較慢之众。另外,他還提供了List接口中沒(méi)有定義的方法依许,專門用于操作表頭和表尾元素棺禾,可以當(dāng)作堆棧、隊(duì)列和雙向隊(duì)列使用悍手。
6.??HashMap帘睦、HashTable、LinkedHashMap和TreeMap用法和區(qū)別坦康?
????????Java為數(shù)據(jù)結(jié)構(gòu)中的映射定義了一個(gè)接口java.util.Map竣付,它有四個(gè)實(shí)現(xiàn)類,分別是HashMap滞欠、HashTable古胆、LinkedHashMap和TreeMap。本節(jié)實(shí)例主要介紹這4中實(shí)例的用法和區(qū)別筛璧。
關(guān)鍵技術(shù)剖析:
Map用于存儲(chǔ)鍵值對(duì)逸绎,根據(jù)鍵得到值,因此不允許鍵重復(fù)夭谤,值可以重復(fù)棺牧。
????????(1)HashMap是一個(gè)最常用的Map,它根據(jù)鍵的hashCode值存儲(chǔ)數(shù)據(jù)朗儒,根據(jù)鍵可以直接獲取它的值颊乘,具有很快的訪問(wèn)速度。HashMap最多只允許一條記錄的鍵為null醉锄,不允許多條記錄的值為null乏悄。HashMap不支持線程的同步,即任一時(shí)刻可以有多個(gè)線程同時(shí)寫(xiě)HashMap恳不,可能會(huì)導(dǎo)致數(shù)據(jù)的不一致檩小。如果需要同步,可以用Collections.synchronizedMap(HashMap map)方法使HashMap具有同步的能力烟勋。
????????(2)Hashtable與HashMap類似规求,不同的是:它不允許記錄的鍵或者值為空;它支持線程的同步神妹,即任一時(shí)刻只有一個(gè)線程能寫(xiě)Hashtable颓哮,然而,這也導(dǎo)致了Hashtable在寫(xiě)入時(shí)會(huì)比較慢鸵荠。
????????(3)LinkedHashMap像HashMap一樣允許null key冕茅,內(nèi)部通過(guò)維護(hù)一個(gè)雙向鏈表,當(dāng)?shù)敵鰰r(shí)可以以插入順序(通常情況下是插入順序,還可以是訪問(wèn)順序)輸出姨伤,因此性能稍微比HashMap低一點(diǎn)哨坪。當(dāng)重新插入一條數(shù)據(jù)(也就是put一個(gè)已經(jīng)存在的key)時(shí)不會(huì)影響插入順序。LinkedHashMap讓用戶從未指定的乍楚、混亂順序的Map實(shí)現(xiàn)HashMap和HashTable中解脫出來(lái)当编。非同步的。并且與TreeMap相比徒溪,沒(méi)有增加插入代價(jià)忿偷。
????????(4)TreeMap能夠把它保存的記錄根據(jù)鍵排序,默認(rèn)是按升序排序臊泌,也可以指定排序的比較器鲤桥。當(dāng)用Iteraor遍歷TreeMap時(shí),得到的記錄是排過(guò)序的渠概。TreeMap的鍵和值都不能為空茶凳。非同步的。
? ? ? ? TreeMap 的底層就是一顆紅黑樹(shù)播揪,它的 containsKey , get , put and remove 方法的時(shí)間復(fù)雜度是 log(n) 贮喧,并且它是按照 key 的自然順序(或者指定排序)排列,與 LinkedHashMap 不同猪狈, LinkedHashMap 保證了元素是按照插入的順序排列箱沦。
? ??????TreeMap取出來(lái)的是排序后的鍵值對(duì)。但如果您要按自然順序或自定義順序遍歷鍵雇庙,那么TreeMap會(huì)更好饱普。
? ??????LinkedHashMap 是HashMap的一個(gè)子類,如果需要輸出的順序和輸入的相同
7.? 為什么用HashMap状共?
????????HashMap 是一個(gè)散列桶(數(shù)組和鏈表),它存儲(chǔ)的內(nèi)容是鍵值對(duì) key-value 映射
????????HashMap 采用了數(shù)組和鏈表的數(shù)據(jù)結(jié)構(gòu)谁帕,能在查詢和修改方便繼承了數(shù)組的線性查找和鏈表的尋址修改
????????HashMap 是非 synchronized峡继,所以 HashMap 很快
????????HashMap 可以接受 null 鍵和值,而 Hashtable 則不能(原因就是 equlas() 方法需要對(duì)象匈挖,因?yàn)?HashMap 是后出的 API 經(jīng)過(guò)處理才可以)
8.? 有什么方法可以減少碰撞碾牌?
????????擾動(dòng)函數(shù)可以減少碰撞
????????原理是如果兩個(gè)不相等的對(duì)象返回不同的 hashcode 的話,那么碰撞的幾率就會(huì)小些儡循。這就意味著存鏈表結(jié)構(gòu)減小舶吗,這樣取值的話就不會(huì)頻繁調(diào)用 equal 方法,從而提高 HashMap 的性能(擾動(dòng)即 Hash 方法內(nèi)部的算法實(shí)現(xiàn)择膝,目的是讓不同對(duì)象返回不同hashcode)誓琼。
????????使用不可變的、聲明作 final 對(duì)象,并且采用合適的 equals() 和 hashCode() 方法腹侣,將會(huì)減少碰撞的發(fā)生
????????不可變性使得能夠緩存不同鍵的 hashcode叔收,這將提高整個(gè)獲取對(duì)象的速度,使用 String傲隶、Integer 這樣的 wrapper 類作為鍵是非常好的選擇饺律。
????????為什么 String、Integer 這樣的 wrapper 類適合作為鍵跺株?
????????因?yàn)?String 是 final复濒,而且已經(jīng)重寫(xiě)了 equals() 和 hashCode() 方法了。不可變性是必要的乒省,因?yàn)闉榱艘?jì)算 hashCode()巧颈,就要防止鍵值改變,如果鍵值在放入時(shí)和獲取時(shí)返回不同的 hashcode 的話作儿,那么就不能從 HashMap 中找到你想要的對(duì)象洛二。
9.? HashMap 中 hash 函數(shù)怎么是實(shí)現(xiàn)的?
????????在 hashmap 中要找到某個(gè)元素,需要根據(jù) key 的 hash 值來(lái)求得對(duì)應(yīng)數(shù)組中的位置攻锰。如何計(jì)算這個(gè)位置就是 hash 算法晾嘶。
? ? ? ? hashmap 的數(shù)據(jù)結(jié)構(gòu)是數(shù)組和鏈表的結(jié)合,所以我們當(dāng)然希望這個(gè) hashmap 里面的元素位置盡量的分布均勻些娶吞,盡量使得每個(gè)位置上的元素?cái)?shù)量只有一個(gè)垒迂。那么當(dāng)我們用 hash 算法求得這個(gè)位置的時(shí)候,馬上就可以知道對(duì)應(yīng)位置的元素就是我們要的妒蛇,而不用再去遍歷鏈表机断。 所以,我們首先想到的就是把 hashcode 對(duì)數(shù)組長(zhǎng)度取模運(yùn)算绣夺。這樣一來(lái)吏奸,元素的分布相對(duì)來(lái)說(shuō)是比較均勻的。
? ? ? ?但是“奶账#”運(yùn)算的消耗還是比較大的奋蔚,能不能找一種更快速、消耗更小的方式烈钞?我們來(lái)看看 JDK1.8 源碼是怎么做的
? ? ? 右移16位泊碑,之后按位異或
? ? ? ?高16 bit 不變,低16 bit 和高16 bit 做了一個(gè)異或(得到的 hashcode 轉(zhuǎn)化為32位二進(jìn)制毯欣,前16位和后16位低16 bit和高16 bit做了一個(gè)異或)
? ? ? ?(n·1) & hash = -> 得到下標(biāo)
10.?拉鏈法導(dǎo)致的鏈表過(guò)深馒过,為什么不用二叉查找樹(shù)代替而選擇紅黑樹(shù)?為什么不一直使用紅黑樹(shù)酗钞?
????????之所以選擇紅黑樹(shù)是為了解決二叉查找樹(shù)的缺陷:二叉查找樹(shù)在特殊情況下會(huì)變成一條線性結(jié)構(gòu)(這就跟原來(lái)使用鏈表結(jié)構(gòu)一樣了腹忽,造成層次很深的問(wèn)題)来累,遍歷查找會(huì)非常慢。而紅黑樹(shù)在插入新數(shù)據(jù)后可能需要通過(guò)左旋留凭、右旋佃扼、變色這些操作來(lái)保持平衡。引入紅黑樹(shù)就是為了查找數(shù)據(jù)快蔼夜,解決鏈表查詢深度的問(wèn)題兼耀。我們知道紅黑樹(shù)屬于平衡二叉樹(shù),為了保持“平衡”是需要付出代價(jià)的求冷,但是該代價(jià)所損耗的資源要比遍歷線性鏈表要少瘤运。所以當(dāng)長(zhǎng)度大于8的時(shí)候,會(huì)使用紅黑樹(shù)匠题;如果鏈表長(zhǎng)度很短的話拯坟,根本不需要引入紅黑樹(shù),引入反而會(huì)慢韭山。
11. 說(shuō)說(shuō)你對(duì)紅黑樹(shù)的見(jiàn)解郁季?
? ? ?每個(gè)節(jié)點(diǎn)非紅即黑
? ? ?根節(jié)點(diǎn)總是黑色的
? ? ?如果節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的(反之不一定)
? ? 每個(gè)葉子節(jié)點(diǎn)都是黑色的空節(jié)點(diǎn)(NIL節(jié)點(diǎn))
? ? 從根節(jié)點(diǎn)到葉節(jié)點(diǎn)或空子節(jié)點(diǎn)的每條路徑钱磅,必須包含相同數(shù)目的黑色節(jié)點(diǎn)(即相同的黑色高度)
12.?如果 HashMap(jdk8) 的大小超過(guò)了負(fù)載因子(load factor)定義的容量怎么辦梦裂?
????????HashMap 默認(rèn)的負(fù)載因子大小為0.75。也就是說(shuō)盖淡,當(dāng)一個(gè) Map 填滿了75%的 bucket 時(shí)候年柠,和其它集合類一樣(如 ArrayList 等),將會(huì)創(chuàng)建原來(lái) HashMap 大小的兩倍的 bucket 數(shù)組來(lái)重新調(diào)整 Map 大小褪迟,并將原來(lái)的對(duì)象放入新的 bucket 數(shù)組中冗恨。這個(gè)過(guò)程叫作?rehashing。
????????因?yàn)樗{(diào)用 hash 方法找到新的 bucket 位置味赃。這個(gè)值只可能在兩個(gè)地方掀抹,一個(gè)是原下標(biāo)的位置,另一種是在下標(biāo)為?<原下標(biāo)+原容量>?的位置心俗。
13. 重新調(diào)整 HashMap 大小存在什么問(wèn)題嗎渴丸?
????????重新調(diào)整 HashMap 大小的時(shí)候,確實(shí)存在條件競(jìng)爭(zhēng)另凌。
????????因?yàn)槿绻麅蓚€(gè)線程都發(fā)現(xiàn) HashMap 需要重新調(diào)整大小了,它們會(huì)同時(shí)試著調(diào)整大小戒幔。在調(diào)整大小的過(guò)程中吠谢,存儲(chǔ)在鏈表中的元素的次序會(huì)反過(guò)來(lái)。因?yàn)橐苿?dòng)到新的 bucket 位置的時(shí)候诗茎,HashMap 并不會(huì)將元素放在鏈表的尾部工坊,而是放在頭部献汗。這是為了避免尾部遍歷(tail traversing)。如果條件競(jìng)爭(zhēng)發(fā)生了王污,那么就死循環(huán)了罢吃。多線程的環(huán)境下不使用 HashMap。
????????為什么多線程會(huì)導(dǎo)致死循環(huán)昭齐,它是怎么發(fā)生的尿招?
????????HashMap 的容量是有限的。當(dāng)經(jīng)過(guò)多次元素插入阱驾,使得 HashMap 達(dá)到一定飽和度時(shí)就谜,Key 映射位置發(fā)生沖突的幾率會(huì)逐漸提高。這時(shí)候里覆, HashMap 需要擴(kuò)展它的長(zhǎng)度丧荐,也就是進(jìn)行Resize。
????????擴(kuò)容:創(chuàng)建一個(gè)新的 Entry 空數(shù)組喧枷,長(zhǎng)度是原數(shù)組的2倍
rehash:遍歷原 Entry 數(shù)組虹统,把所有的 Entry 重新 Hash 到新數(shù)組
? ? ? (友情鏈接:HashMap擴(kuò)容全過(guò)程)
14、簡(jiǎn)單聊一下HashTable隧甚?
? ? ? ?HashTable的主要方法的源碼實(shí)現(xiàn)邏輯各薇,與HashMap中非常相似,有一點(diǎn)重大區(qū)別就是所有的操作都是通過(guò)synchronized鎖保護(hù)的涧窒。只有獲得了對(duì)應(yīng)的鎖空入,才能進(jìn)行后續(xù)的讀寫(xiě)等操作。
????????數(shù)組 + 鏈表方式存儲(chǔ)
????????默認(rèn)容量:11(質(zhì)數(shù)為宜)
????????put操作:首先進(jìn)行索引計(jì)算 (key.hashCode() & 0x7FFFFFFF)% table.length咖城;若在鏈表中找到了茬腿,則替換舊值,若未找到則繼續(xù)宜雀;當(dāng)總元素個(gè)數(shù)超過(guò) 容量 * 加載因子 時(shí)切平,擴(kuò)容為原來(lái) 2 倍并重新散列;將新元素加到鏈表頭部
????????對(duì)修改 Hashtable 內(nèi)部共享數(shù)據(jù)的方法添加了 synchronized辐董,保證線程安全
15悴品、可以使用 CocurrentHashMap 來(lái)代替 Hashtable 嗎?
????????我們知道 Hashtable 是 synchronized 的简烘,但是 ConcurrentHashMap 同步性能更好苔严,因?yàn)樗鼉H僅根據(jù)同步級(jí)別對(duì) map 的一部分進(jìn)行上鎖
????????ConcurrentHashMap 當(dāng)然可以代替 HashTable,但是 HashTable 提供更強(qiáng)的線程安全性
????????它們都可以用于多線程的環(huán)境孤澎,但是當(dāng) Hashtable 的大小增加到一定的時(shí)候届氢,性能會(huì)急劇下降,因?yàn)榈鷷r(shí)需要被鎖定很長(zhǎng)的時(shí)間覆旭。由于 ConcurrentHashMap 引入了分割(segmentation)退子,不論它變得多么大岖妄,僅僅需要鎖定 Map 的某個(gè)部分,其它的線程不需要等到迭代完成才能訪問(wèn) Map寂祥。簡(jiǎn)而言之荐虐,在迭代的過(guò)程中,ConcurrentHashMap 僅僅鎖定 Map 的某個(gè)部分丸凭,而 Hashtable 則會(huì)鎖定整個(gè) Map
16. ConcurrentHashMap 底層邏輯(JDK 1.7)福扬?
????????ConcurrentHashMap 是由 Segment 數(shù)組和 HashEntry 數(shù)組和鏈表組成
????????Segment 是基于重入鎖(ReentrantLock):一個(gè)數(shù)據(jù)段競(jìng)爭(zhēng)鎖。每個(gè) HashEntry 一個(gè)鏈表結(jié)構(gòu)的元素贮乳,利用 Hash 算法得到索引確定歸屬的數(shù)據(jù)段忧换,也就是對(duì)應(yīng)到在修改時(shí)需要競(jìng)爭(zhēng)獲取的鎖。ConcurrentHashMap 支持 CurrencyLevel(Segment 數(shù)組數(shù)量)的線程并發(fā)向拆。每當(dāng)一個(gè)線程占用鎖訪問(wèn)一個(gè) Segment 時(shí)亚茬,不會(huì)影響到其他的 Segment
????????核心數(shù)據(jù)如 value,以及鏈表都是 volatile 修飾的浓恳,保證了獲取時(shí)的可見(jiàn)性
????????首先是通過(guò) key 定位到 Segment刹缝,之后在對(duì)應(yīng)的 Segment 中進(jìn)行具體的 put 操作如下:
????????將當(dāng)前 Segment 中的 table 通過(guò) key 的 hashcode 定位到 HashEntry。
????????遍歷該 HashEntry颈将,如果不為空則判斷傳入的? key 和當(dāng)前遍歷的 key 是否相等梢夯,相等則覆蓋舊的 value
????????不為空則需要新建一個(gè) HashEntry 并加入到 Segment 中,同時(shí)會(huì)先判斷是否需要擴(kuò)容
????????最后會(huì)解除在 1 中所獲取當(dāng)前 Segment 的鎖晴圾。
????????雖然 HashEntry 中的 value 是用 volatile 關(guān)鍵詞修飾的颂砸,但是并不能保證并發(fā)的原子性,所以 put 操作時(shí)仍然需要加鎖處理
????????首先第一步的時(shí)候會(huì)嘗試獲取鎖死姚,如果獲取失敗肯定就有其他線程存在競(jìng)爭(zhēng)人乓,則利用 scanAndLockForPut() 自旋獲取鎖。
????????嘗試自旋獲取鎖
????????如果重試的次數(shù)達(dá)到了 MAX_SCAN_RETRIES 則改為阻塞鎖獲取都毒,保證能獲取成功色罚。最后解除當(dāng)前 Segment 的鎖
17. ConcurrentHashMap底層邏輯(JDK 1.8)?
????????CocurrentHashMap?拋棄了原有的 Segment 分段鎖账劲,采用了CAS + synchronized來(lái)保證并發(fā)安全性戳护。其中的val next?都用了 volatile 修飾,保證了可見(jiàn)性瀑焦。
????????最大特點(diǎn)是引入了 CAS
????????借助 Unsafe 來(lái)實(shí)現(xiàn) native code腌且。CAS有3個(gè)操作數(shù),內(nèi)存值 V榛瓮、舊的預(yù)期值 A铺董、要修改的新值 B。當(dāng)且僅當(dāng)預(yù)期值 A 和內(nèi)存值 V 相同時(shí)榆芦,將內(nèi)存值V修改為 B柄粹,否則什么都不做。Unsafe 借助 CPU 指令 cmpxchg 來(lái)實(shí)現(xiàn)匆绣。
put 過(guò)程
根據(jù) key 計(jì)算出 hashcode
判斷是否需要進(jìn)行初始化
通過(guò) key 定位出的 Node驻右,如果為空表示當(dāng)前位置可以寫(xiě)入數(shù)據(jù),利用 CAS 嘗試寫(xiě)入崎淳,失敗則自旋保證成功
如果當(dāng)前位置的 hashcode == MOVED == -1,則需要進(jìn)行擴(kuò)容
如果都不滿足堪夭,則利用 synchronized 鎖寫(xiě)入數(shù)據(jù)
如果數(shù)量大于 TREEIFY_THRESHOLD 則要轉(zhuǎn)換為紅黑樹(shù)
get 過(guò)程
根據(jù)計(jì)算出來(lái)的 hashcode 尋址,如果就在桶上那么直接返回值
如果是紅黑樹(shù)那就按照樹(shù)的方式獲取值
就不滿足那就按照鏈表的方式遍歷獲取值
18. hashMap(1.7) 擴(kuò)容的條件是什么拣凹?
? ? ? 數(shù)據(jù)結(jié)構(gòu):數(shù)組 + 鏈表?
????????擴(kuò)容必須滿足兩個(gè)條件:
? ??????1森爽、 存放新值的時(shí)候當(dāng)前已有元素的個(gè)數(shù)必須大于等于閾值
? ??????2、 存放新值的時(shí)候當(dāng)前存放數(shù)據(jù)發(fā)生hash碰撞(當(dāng)前key計(jì)算的hash值換算出來(lái)的數(shù)組下標(biāo)位置已經(jīng)存在值)
? ? ?最少12個(gè)元素嚣镜,最多27個(gè)元素? ?12+15個(gè)
19. hashMap(1.8) 擴(kuò)容的條件是什么爬迟?
? ? ? ?數(shù)據(jù)結(jié)構(gòu):數(shù)組 + ( 鏈表 or 紅黑樹(shù) )?
???????擴(kuò)容必須滿足條件:
? ? ? ?1、 存放新值的時(shí)候當(dāng)前已有元素的個(gè)數(shù)必須大于閾值
? ? ?當(dāng)?shù)?3個(gè)元素進(jìn)來(lái)時(shí)菊匿,發(fā)生擴(kuò)容
20. hashMap(1.8)源碼字段分析 ?
//默認(rèn)table數(shù)組buckets的數(shù)目付呕,必須是2的平方,默認(rèn)值是16??
static?final?int?DEFAULT_INITIAL_CAPACITY?=?1?<<?4;? ?
//默認(rèn)table最大的長(zhǎng)度約10億多(1073741824)最大的buckets數(shù)目??
static?final?int?MAXIMUM_CAPACITY?=?1?<<?30;??
//默認(rèn)負(fù)載因子??
static?final?float?DEFAULT_LOAD_FACTOR?=?0.75f;??
//當(dāng)單個(gè)鏈表的個(gè)數(shù)超過(guò)8個(gè)節(jié)點(diǎn)就轉(zhuǎn)化為紅黑樹(shù)存儲(chǔ)??
static?final?int?TREEIFY_THRESHOLD?=?8;??
//如果原來(lái)是紅黑樹(shù)跌捆,后來(lái)被刪除一些節(jié)點(diǎn)后徽职,只剩小于等于6個(gè),會(huì)被重新轉(zhuǎn)成鏈表存儲(chǔ)??
static?final?int?UNTREEIFY_THRESHOLD?=?6;??
//當(dāng)數(shù)組的長(zhǎng)度(注意不是map的size而是table.length)大于64的時(shí)候佩厚,??
//會(huì)對(duì)單個(gè)桶里大于8的鏈表進(jìn)行樹(shù)化??
static?final?int?MIN_TREEIFY_CAPACITY?=?64;??
//用來(lái)實(shí)現(xiàn)遍歷map的set,依次遍歷table中所有桶中的node或者treeNode??
transient?Set<Map.Entry<K,V>>?entrySet;??
//當(dāng)前存儲(chǔ)的實(shí)際數(shù)據(jù)量=map.size而不是table.length??
transient?int?size;??
//修改次數(shù)姆钉,用來(lái)判斷是否該map同時(shí)被多個(gè)線程操作,??
//多線程環(huán)境下會(huì)拋出異常ConcurrentModificationException??
transient?int?modCount;??
//當(dāng)前數(shù)組中的閾值?table.length?*?loadFactor抄瓦,如果超 過(guò)這個(gè)閾值潮瓶,就要進(jìn)行擴(kuò)容?(resize)??
int?threshold;??
//負(fù)載因子??
final?float?loadFactor;??