成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

「Java」HashMap底層實(shí)現(xiàn)、加載因子、容量值及死循環(huán)

開發(fā) 后端
HashMap是一個(gè)基于哈希表實(shí)現(xiàn)的無序的key-value容器,它鍵和值允許設(shè)置為 null,同時(shí)它是線程不安全的。

HashMap 簡(jiǎn)介

HashMap是一個(gè)基于哈希表實(shí)現(xiàn)的無序的key-value容器,它鍵和值允許設(shè)置為 null,同時(shí)它是線程不安全的。

HashMap 底層實(shí)現(xiàn)

  •  在jdk 1.7中HashMap是以數(shù)組+鏈表的實(shí)現(xiàn)的
  •  在jdk1.8開始引入紅黑樹,HashMap底層變成了數(shù)組+鏈表+紅黑樹實(shí)現(xiàn)

紅黑樹簡(jiǎn)介

紅黑樹是一種特殊的平衡二叉樹,它有如下的特征:

  •  節(jié)點(diǎn)是紅色或黑色
  •  根節(jié)點(diǎn)是黑色的
  •  所有葉子都是黑色。(葉子是NULL節(jié)點(diǎn))
  •  每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色的(從每個(gè)葉子到根的所有路徑上不能有兩個(gè)連續(xù)的紅色節(jié)點(diǎn))
  •  從任一節(jié)點(diǎn)到其每個(gè)葉子的所有路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。

所以紅黑樹的時(shí)間復(fù)雜度為: O(lgn)。

jdk1.8:數(shù)組+鏈表+紅黑樹

HashMap的底層首先是一個(gè)數(shù)組,元素存放的數(shù)組索引值就是由該元素的哈希值(key-value中key的哈希值)確定的,這就可能產(chǎn)生一種特殊情況——不同的key哈希值相同。

在這樣的情況下,于是引入鏈表,如果key的哈希值相同,在數(shù)組的該索引中存放一個(gè)鏈表,這個(gè)鏈表就包含了所有key的哈希值相同的value值,這就解決了哈希沖突的問題。

但是如果發(fā)生大量哈希值相同的特殊情況,導(dǎo)致鏈表很長,就會(huì)嚴(yán)重影響HashMap的性能,因?yàn)殒湵淼牟樵冃市枰闅v所有節(jié)點(diǎn)。于是在jdk1.8引入了紅黑樹,當(dāng)鏈表的長度大于8,且HashMap的容量大于64的時(shí)候,就會(huì)將鏈表轉(zhuǎn)化為紅黑樹。 

  1. // jdk1.8  
  2. // HashMap#putVal  
  3. // binCount 是該鏈表的長度計(jì)數(shù)器,當(dāng)鏈表長度大于等于8時(shí),執(zhí)行樹化方法  
  4. // TREEIFY_THRESHOLD = 8  
  5. if (binCount >= TREEIFY_THRESHOLD - 1)  
  6.     treeifyBin(tab, hash);  
  7. // HashMap#treeifyBin      
  8. final void treeifyBin(Node<K,V>[] tab, int hash) {  
  9.     int n, index; Node<K,V> e;  
  10.     // MIN_TREEIFY_CAPACITY=64  
  11.     // 若 HashMap 的大小小于64,僅擴(kuò)容,不樹化  
  12.     if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY 
  13.         resize();  
  14.     else if ((e = tab[index = (n - 1) & hash]) != null) {  
  15.         TreeNode<K,V> hd = nulltl = null 
  16.         do { 
  17.              TreeNode<K,V> p = replacementTreeNode(e, null);  
  18.             if (tl == null)  
  19.                 hd = p
  20.              else {  
  21.                 p.prev = tl
  22.                  tl.next = p 
  23.             }  
  24.             tl = p 
  25.         } while ((ee = e.next) != null);  
  26.         if ((tab[index] = hd) != null)  
  27.             hd.treeify(tab);  
  28.     }  

加載因子為什么是0.75

所謂的加載因子,也叫擴(kuò)容因子或者負(fù)載因子,它是用來進(jìn)行擴(kuò)容判斷的。

假設(shè)加載因子是0.5,HashMap初始化容量是16,當(dāng)HashMap中有16 * 0.5=8個(gè)元素時(shí),HashMap就會(huì)進(jìn)行擴(kuò)容操作。

而HashMap中加載因子為0.75,是考慮到了性能和容量的平衡。

由加載因子的定義,可以知道它的取值范圍是(0, 1]。

  •  如果加載因子過小,那么擴(kuò)容門檻低,擴(kuò)容頻繁,這雖然能使元素存儲(chǔ)得更稀疏,有效避免了哈希沖突發(fā)生,同時(shí)操作性能較高,但是會(huì)占用更多的空間。
  •  如果加載因子過大,那么擴(kuò)容門檻高,擴(kuò)容不頻繁,雖然占用的空間降低了,但是這會(huì)導(dǎo)致元素存儲(chǔ)密集,發(fā)生哈希沖突的概率大大提高,從而導(dǎo)致存儲(chǔ)元素的數(shù)據(jù)結(jié)構(gòu)更加復(fù)雜(用于解決哈希沖突),最終導(dǎo)致操作性能降低。
  •  還有一個(gè)因素是為了提升擴(kuò)容效率。因?yàn)镠ashMap的容量(size屬性,構(gòu)造函數(shù)中的initialCapacity變量)有一個(gè)要求:它一定是2的冪。所以加載因子選擇了0.75就可以保證它與容量的乘積為整數(shù)。 
  1. // 構(gòu)造函數(shù)  
  2. public HashMap(int initialCapacity, float loadFactor) {  
  3.     // ……  
  4.     this.loadFactor = loadFactor;// 加載因子  
  5.     this.threshold = tableSizeFor(initialCapacity);  
  6.  
  7. /**  
  8.  * Returns a power of two size for the given target capacity.返回2的冪  
  9.  * MAXIMUM_CAPACITY = 1 << 30  
  10.  */  
  11. static final int tableSizeFor(int cap) {  
  12.     int n = cap - 1;  
  13.     n |= n >>> 1;  
  14.     n |= n >>> 2; 
  15.     n |= n >>> 4;  
  16.     n |= n >>> 8;  
  17.     n |= n >>> 16;  
  18.     return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;  

HashMap 的容量為什么是2的 n 次冪

HashMap的默認(rèn)初始容量是16,而每次擴(kuò)容是擴(kuò)容為原來的2倍。這里的16和2倍就保證了HashMap的容量是2的n次冪,那么這樣設(shè)計(jì)的原因是什么呢?

原因一:與運(yùn)算高效

與運(yùn)算&,基于二進(jìn)制數(shù)值,同時(shí)為1結(jié)果為1,否則就是0。如1&1=1,1&0=0,0&0=0。使用與運(yùn)算的原因就是對(duì)于計(jì)算機(jī)來說,與運(yùn)算十分高效。

原因二:有利于元素充分散列,減少 Hash 碰撞

在給HashMap添加元素的putVal函數(shù)中,有這樣一段代碼: 

  1. // n為容量,hash為該元素的hash值  
  2. if ((p = tab[i = (n - 1) & hash]) == null)  
  3.     tab[i] = newNode(hash, key, value, null); 

它會(huì)在添加元素時(shí),通過i = (n - 1) & hash計(jì)算該元素在HashMap中的位置。

當(dāng) HashMap 的容量為 2 的 n 次冪時(shí),他的二進(jìn)制值是100000……(n個(gè)0),所以n-1的值就是011111……(n個(gè)1),這樣的話(n - 1) & hash的值才能夠充分散列。

舉個(gè)例子,假設(shè)容量為16,現(xiàn)在有哈希值為1111,1110,1011,1001四種將被添加,它們與n-1(15的二進(jìn)制=01111)的哈希值分別為1111、1110、1110、1011,都不相同。

而假設(shè)容量不為2的n次冪,假設(shè)為10,那么它與上述四個(gè)哈希值進(jìn)行與運(yùn)算的結(jié)果分別是:0101、0100、0001、0001。

可以看到后兩個(gè)值發(fā)生了碰撞,從中可以看出,非2的n次冪會(huì)加大哈希碰撞的概率。所以 HashMap 的容量設(shè)置為2的n次冪有利于元素的充分散列。

參考:HashMap初始容量為什么是2的n次冪及擴(kuò)容為什么是2倍的形式

HashMap 是如何導(dǎo)致死循環(huán)的

HashMap會(huì)導(dǎo)致死循環(huán)是在jdk1.7中,由于擴(kuò)容時(shí)的操作是使用頭插法,在多線程的環(huán)境下可能產(chǎn)生循環(huán)鏈表,由此導(dǎo)致了死循環(huán)。在jdk1.8中改為使用尾插法,避免了該死循環(huán)的情況。 

 

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2013-06-06 13:34:56

HashMap線程不安全

2020-12-17 07:39:30

HashMap死循環(huán)數(shù)據(jù)

2023-01-04 07:54:03

HashMap底層JDK

2025-01-21 00:00:00

HashMap死循環(huán)數(shù)據(jù)損壞

2020-09-29 15:24:07

面試數(shù)據(jù)結(jié)構(gòu)Hashmap

2022-01-20 08:44:25

HashMap死循環(huán)開放性

2022-01-18 06:59:50

HashMap循環(huán)底層

2023-07-11 08:00:00

2023-01-31 08:24:55

HashMap死循環(huán)

2022-01-13 06:59:40

HashMap底層面試

2011-08-29 16:23:29

Lua腳本

2015-10-09 09:43:53

云環(huán)境CPU虛擬化底層容量

2021-08-29 07:41:48

數(shù)據(jù)HashMap底層

2023-10-18 10:55:55

HashMap

2020-08-19 16:36:53

HashMap紅黑樹閾值

2011-09-07 10:13:04

IPv6IPv4

2018-10-10 20:20:14

2024-12-06 16:00:00

C++頭文件

2013-06-06 13:10:44

HashMap無鎖

2024-10-30 11:30:02

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲va在线va天堂va狼色在线 | 国产成人精品a视频一区www | 亚洲入口| h视频在线观看免费 | 免费视频久久久久 | 亚洲精品一区二区三区四区高清 | 91国内外精品自在线播放 | 麻豆91精品91久久久 | 国产精品一区二区三级 | 成人一区精品 | caoporn国产精品免费公开 | 日本激情视频在线播放 | 久久久精品网站 | 国产成人网 | 日韩欧美不卡 | 1级毛片 | 欧美国产日韩一区二区三区 | 国产黄色网 | 99精品热视频 | 国产精品一区二区三区在线 | 中文字幕91av | 国产精品福利一区二区三区 | 久久99精品久久久久 | 围产精品久久久久久久 | 国产探花 | 国产小视频在线观看 | 人人玩人人添人人澡欧美 | 久久久久国产精品 | 欧州一区二区三区 | 欧美做暖暖视频 | 日韩精品亚洲专区在线观看 | 精品在线免费看 | 亚洲国产欧美国产综合一区 | 黄片毛片 | 在线观看黄色电影 | 欧美激情免费在线 | 免费在线观看av网址 | 亚洲精品一区在线观看 | 欧美国产视频 | 亚洲黄色成人网 | 看羞羞视频免费 |