最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java的HashMap源碼解析

 更新時(shí)間:2023年11月15日 10:11:55   作者:龍三丶  
這篇文章主要介紹了Java的HashMap源碼解析,HashMap是一個(gè)用于存儲(chǔ)Key-Value鍵值對(duì)的集合,每一個(gè)鍵值對(duì)是一個(gè)Node,后臺(tái)是用一個(gè)Node數(shù)組來(lái)存放數(shù)據(jù),這個(gè)Node數(shù)組就是HashMap的主干,需要的朋友可以參考下

前言

以jdk1.8為例,HashMap是一個(gè)用于存儲(chǔ)Key-Value鍵值對(duì)的集合,每一個(gè)鍵值對(duì)是一個(gè)Node(jdk1.7叫做Entry)。后臺(tái)是用一個(gè)Node數(shù)組來(lái)存放數(shù)據(jù),這個(gè)Node數(shù)組就是HashMap的主干。

這里我們主要來(lái)分析HashMap的get和put方法。

put

public V put(K key, V value) {
	    	return putVal(hash(key), key, value, false, true);
	}
 
	final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
				   boolean evict) {
		Node<K,V>[] tab; Node<K,V> p; int n, i;
		//如果是第一次put,就進(jìn)行數(shù)組的大小初始化,默認(rèn)是16
		if ((tab = table) == null || (n = tab.length) == 0)
			n = (tab = resize()).length;
		//根據(jù)hash值,找到在數(shù)組中的位置,如果此位置沒(méi)有值,就new一個(gè)新的node插入
		if ((p = tab[i = (n - 1) & hash]) == null)
			tab[i] = newNode(hash, key, value, null);
		//如果數(shù)組該位置有值
		else {
			Node<K,V> e; K k;
			//判斷該位置節(jié)點(diǎn)的key是否和即將插入的key相等,相等就取出來(lái)等待覆蓋
			if (p.hash == hash &&
					((k = p.key) == key || (key != null && key.equals(k))))
				e = p;
			//若果該節(jié)點(diǎn)是紅黑樹(shù),則調(diào)用紅黑樹(shù)相關(guān)方法
			else if (p instanceof TreeNode)
				e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
			//到了這里,說(shuō)明該節(jié)點(diǎn)是鏈表,調(diào)用鏈表相關(guān)方法
			else {
				for (int binCount = 0; ; ++binCount) {
					//循環(huán)到最后一個(gè)節(jié)點(diǎn),然后插入新節(jié)點(diǎn)(1.7是往頭結(jié)點(diǎn)插入,1.8是往尾部插入)
					if ((e = p.next) == null) {
						p.next = newNode(hash, key, value, null);
						//判斷插入后的該鏈表的長(zhǎng)度,如果大于8,就轉(zhuǎn)成紅黑樹(shù)
						if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
							treeifyBin(tab, hash);
						break;
					}
					//這里表示鏈表中某個(gè)節(jié)點(diǎn)的key與即將插入的key相等,就跳出循環(huán)等待覆蓋
					if (e.hash == hash &&
							((k = e.key) == key || (key != null && key.equals(k))))
						break;
					p = e;
				}
			}
			//這里表示有節(jié)點(diǎn)的key與新的key相等,那么就覆蓋
			if (e != null) {
				V oldValue = e.value;
				if (!onlyIfAbsent || oldValue == null)
					e.value = value;
				afterNodeAccess(e);
				return oldValue;
			}
		}
		++modCount;
		//插入完之后,如果導(dǎo)致size超過(guò)了預(yù)設(shè)的閾值,就進(jìn)行擴(kuò)容(1.7是插入前判斷,1.8是插入后判斷)
		if (++size > threshold)
			resize();
		afterNodeInsertion(evict);
		return null;
	}

擴(kuò)容步驟:

1、創(chuàng)建一個(gè)原數(shù)組兩倍大小的新數(shù)組,并且把閾值擴(kuò)大一倍。

2、遍歷原數(shù)組,進(jìn)行數(shù)據(jù)遷移。分為紅黑樹(shù)和鏈表兩種情況。

 好了,擴(kuò)容部分就不展開(kāi)代碼詳細(xì)說(shuō)明,接下來(lái)進(jìn)入get方法,相較于put方法就沒(méi)那么復(fù)雜了,且代碼量也比較少

get

public V get(Object key) {
		Node<K,V> e;
		return (e = getNode(hash(key), key)) == null ? null : e.value;
	}
	final Node<K,V> getNode(int hash, Object key) {
		Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
		//判斷底層node數(shù)組是否為空及該hash值對(duì)應(yīng)的數(shù)組位置是否有值
		if ((tab = table) != null && (n = tab.length) > 0 &&
				(first = tab[(n - 1) & hash]) != null) {
			//判斷該數(shù)組的節(jié)點(diǎn)是不是我們需要的值,是就直接返回
			if (first.hash == hash &&
					((k = first.key) == key || (key != null && key.equals(k))))
				return first;
			if ((e = first.next) != null) {
				//該節(jié)點(diǎn)是紅黑樹(shù)則直接調(diào)用紅黑樹(shù)的遍歷方法
				if (first instanceof TreeNode)
					return ((TreeNode<K,V>)first).getTreeNode(hash, key);
				//遍歷鏈表
				do {
					if (e.hash == hash &&
							((k = e.key) == key || (key != null && key.equals(k))))
						//是我們需要的值,返回
						return e;
				} while ((e = e.next) != null);
			}
		}
		return null;
	}

注意:

1、HashMap底層就是用一個(gè)個(gè)的Node來(lái)存儲(chǔ)單個(gè)數(shù)據(jù),每個(gè)Node有hash值、key、value、及指向下一個(gè)Node的引用(next)。Node數(shù)組中就是所有鏈表的頭節(jié)點(diǎn)。

2、當(dāng)出現(xiàn)hash沖突的情況,原Node的next就會(huì)指向新插入的Node,也就是形成了鏈表。

3、每次擴(kuò)容的長(zhǎng)度必須是2的冪,因?yàn)椋鶕?jù)key的hash值計(jì)算出的數(shù)組索引應(yīng)盡量不要重復(fù),實(shí)現(xiàn)均勻分布,均勻分布的話大部分查找的數(shù)據(jù)都是以數(shù)組的形式查找,就不會(huì)蛻變成鏈表,而數(shù)組的查找效率比鏈表高很多。

4、影響擴(kuò)容的因素有兩個(gè):數(shù)組的長(zhǎng)度(DEFAULT_INITIAL_CAPACITY)和負(fù)載因子(DEFAULT_LOAD_FACTOR),當(dāng)這兩個(gè)相乘大于等于當(dāng)前HashMap的Size時(shí),就進(jìn)行擴(kuò)容

5、擴(kuò)容在并發(fā)情況下可能會(huì)形成鏈表環(huán),存在并發(fā)安全問(wèn)題,這點(diǎn)需要注意

6、當(dāng)鏈表的節(jié)點(diǎn)超過(guò)8個(gè)時(shí),會(huì)轉(zhuǎn)成紅黑樹(shù),鏈表的時(shí)間復(fù)雜度為O(n),而紅黑樹(shù)為O(logn)

到此這篇關(guān)于Java的HashMap源碼解析的文章就介紹到這了,更多相關(guān)HashMap源碼 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SSM框架+Plupload實(shí)現(xiàn)分塊上傳大文件示例

    SSM框架+Plupload實(shí)現(xiàn)分塊上傳大文件示例

    這篇文章主要介紹了SSM框架+Plupload實(shí)現(xiàn)分塊上傳示例(Spring+SpringMVC+MyBatis+Plupload),將用戶選中的文件(可多個(gè))分隔成一個(gè)個(gè)小塊,依次向服務(wù)器上傳,有興趣的可以了解一下。
    2017-03-03
  • Springboot項(xiàng)目啟動(dòng)成功后可通過(guò)五種方式繼續(xù)執(zhí)行

    Springboot項(xiàng)目啟動(dòng)成功后可通過(guò)五種方式繼續(xù)執(zhí)行

    本文主要介紹了Springboot項(xiàng)目啟動(dòng)成功后可通過(guò)五種方式繼續(xù)執(zhí)行,主要包括CommandLineRunner接口,ApplicationRunner接口,ApplicationListener接口,@PostConstruct注解,InitalizingBean接口,感興趣的可以了解一下
    2023-12-12
  • Java Eclipse中實(shí)現(xiàn)快速替換變量

    Java Eclipse中實(shí)現(xiàn)快速替換變量

    這篇文章主要介紹了Java Eclipse中實(shí)現(xiàn)快速替換變量,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解

    Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解

    這篇文章主要介紹了Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-07-07
  • default怎么修飾接口中的方法詳解

    default怎么修飾接口中的方法詳解

    今天給各位小伙伴們總結(jié)一下default怎么修飾接口中的方法,文中有非常詳細(xì)的圖文解說(shuō).對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • 解決mybatis中的mapper命名問(wèn)題

    解決mybatis中的mapper命名問(wèn)題

    這篇文章主要介紹了解決mybatis中的mapper命名問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 深入理解ContextClassLoader加載器

    深入理解ContextClassLoader加載器

    這篇文章主要介紹了深入理解ContextClassLoader加載器,Thread?context?class?loader存在的目的主要是為了解決parent?delegation機(jī)制下無(wú)法干凈的解決的問(wèn)題,需要的朋友可以參考下
    2023-10-10
  • SpringBoot配置RocketMQ的詳細(xì)過(guò)程

    SpringBoot配置RocketMQ的詳細(xì)過(guò)程

    這篇文章主要介紹了SpringBoot配置RocketMQ的詳細(xì)過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • SpringBoot熱部署設(shè)置方法詳解

    SpringBoot熱部署設(shè)置方法詳解

    在實(shí)際開(kāi)發(fā)中,每次修改代碼就需要重啟項(xiàng)目,重新部署,對(duì)于一個(gè)后端開(kāi)發(fā)者來(lái)說(shuō),重啟確實(shí)很難受。在java開(kāi)發(fā)領(lǐng)域,熱部署一直是一個(gè)難以解決的問(wèn)題,目前java虛擬機(jī)只能實(shí)現(xiàn)方法體的熱部署,對(duì)于整個(gè)類(lèi)的結(jié)構(gòu)修改,仍然需要重啟項(xiàng)目
    2022-10-10
  • java使用OpenCV從視頻文件中獲取幀

    java使用OpenCV從視頻文件中獲取幀

    這篇文章主要為大家詳細(xì)介紹了java使用OpenCV從視頻文件中獲取幀,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07

最新評(píng)論

云阳县| 柳林县| 甘孜县| 炉霍县| 涿鹿县| 崇信县| 孟津县| 镇平县| 建宁县| 民乐县| 本溪市| 师宗县| 化隆| 红河县| 深州市| 全南县| 许昌县| 枝江市| 黄冈市| 泊头市| 文登市| 诸暨市| 肃宁县| 沅陵县| 麻城市| 吴忠市| 米脂县| 南部县| 嘉祥县| 德化县| 天全县| 舟山市| 布拖县| 林州市| 饶阳县| 马山县| 梅河口市| 突泉县| 宣化县| 苍溪县| 仙游县|