日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java容器 | 基于源码分析Map集合体系

發布時間:2025/3/17 java 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java容器 | 基于源码分析Map集合体系 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、容器之Map集合

集合體系的源碼中,Map中的HashMap的設計堪稱最經典,涉及數據結構、編程思想、哈希計算等等,在日常開發中對于一些源碼的思想進行參考借鑒還是很有必要的。

  • 基礎:元素增查刪、容器信息;
  • 進階:存儲結構、容量、哈希;

API體系

在整個Map和Set的API體系中,最重要的就是HashMap的實現原理:

  • HashMap:基于哈希表管理元素;
  • LinkedHashMap:基于HashMap和雙向鏈表;
  • HashSet:底層維護HashMap結構;
  • LinkedHashSet:繼承HashSet,雙向鏈表;

所以Map和Set的系列中,除特殊API之外,基本原理都依賴HashMap,只是在各自具體實現時,適用于不同特點的元素管理。

二、數據結構

在看HashMap之前,先理解一種數據結構:數組+鏈表的結構。

基于數組管理元素的位置,元素的存儲形成鏈表結構,既然是鏈表那么就可以是單雙向的結構,這需要針對具體的API去分析,通過這個結構可以得到幾個關鍵信息:

  • 擴容:基于數組則面對擴容問題;
  • 鏈表:形成鏈表結構的機制;
  • 哈希:哈希值計算與沖突處理;

三、HashMap詳解

1、結構封裝

既然上面簡單描述了數組+鏈表的結構,那么從源碼角度看看是如何封裝的:

transient Node<K,V>[] table;

在HashMap中數組結構的變量命名為table(表),并且是基于Node<K,V>的節點:

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next; }

實現Map.Entry接口,并定義節點的結構變量,和節點自身的相關方法。

2、構造方法

在知道HashMap中的基礎結構后,可以看其相關的構造方法,初始化哪些變量:

無參構造

public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; }
  • float DEFAULT_LOAD_FACTOR = 0.75f;
  • this.loadFactor = DEFAULT_LOAD_FACTOR;

實際上還要關注一個核心參數:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16

即數組默認的初始化容量DEFAULT_INITIAL_CAPACITY為16,擴容的閾值loadFactor為0.75,即表示當數組中元素達到12個便會進行擴容操作。

有參構造

當然也可以通過有參構造方法去設置兩個參數:即容量和擴容的閾值:

public HashMap(int initialCapacity, float loadFactor) ;

通過兩個構造方法的源碼可知:當直接創建新的HashMap的時候,不會立即對哈希數組進行初始化,但是可以對關鍵變量做自定義設置。

3、裝載元素

順著HashMap的使用方法,看元素添加:

public V put(K key, V value) {return putVal(hash(key), key, value, false, true); }

在put的時候并沒有做過多直接操作,而是調用兩個關鍵方法:

  • hash():計算key的hash值;
  • putVal():元素添加過程;

這里必須看一個關鍵方法,哈希值的計算:

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }

并不是直接獲取Object中hashCode的返回值,計算key對應的hashCode值,和hashCode值右移16位的值,并對兩個結果進行異或運算,以此拉低哈希沖突發生的概率。

再看putVal()方法,這里的操作就相當精彩:

核心步驟總結:

  • 首次執行判斷并初始化底層數組;
  • 基于哈希值計算結果添加元素;
  • 根據添加元素后的容量來判斷是否擴容;

這里還需要說明一個問題:

HashMap基于紅黑樹來處理哈希沖突問題,如果hash沖突過多,對O(n)的查詢性能的影響非常大,當沖突節點鏈表的沖突元素數量到達8時,并且數組的長度到達64時,會使用紅黑樹結構代替鏈表來處理哈希沖突的查詢性能問題,關于樹結構可以移步之前的相關文章。

4、自動化擴容

容器在一定邊界內可以不斷添加元素,其核心的機制就是擴容,HashMap的擴容遵循最小可用原則,當然容量到達閾值,便會觸發自動擴容機制。

閾值:threshold=capacity*loadFactor,默認即 16*0.75=12。

核心方法:resize;

核心步驟總結:

  • 判斷擴容的邊界參數:threshold;
  • 核心參數計算:容量和閾值;
  • 基于新參數創建一個新的空數組;
  • 原數組為null則過程可以理解為初始化;
  • 原數組不為null則擴容并遷移數據;

很顯然如果涉及數組擴容則會很影響效率,所以在日常開發中,可以在使用HashMap的時候預先估計好HashMap的大小,保證閾值大于存儲的元素數量,盡可能避免進行多次擴容操作。

5、查詢元素

getNode查找方法,通過hash值的計算,然后依次經過數組、紅黑樹、鏈表進行遍歷查詢:

6、刪除元素

removeNode刪除方法,首先通過hash值的計算,找到要刪除的節點,然后判斷索引位置是紅黑樹還是鏈表結構,分別執行各自的刪除流程:

7、補充說明

這里對兩個方法做個簡單的說明:hashCode()與equals(),通常來說重寫equals方法的時候需要重寫hashCode方法。

這兩個方法都可以用來比較兩個對象是否相等,但是hash值有存在沖突的情況,可能存在兩個對象的hash值沖突,這時候可以通過equals判斷對象值是否相同,==判斷值對象,地址判斷引用對象。

在HashMap的結構中,鏈表上的hash值相同情況還要通過equals方法來判斷具體值是否相同,才能找到相應的對象。

四、源代碼地址

GitHub·地址 https://github.com/cicadasmile/java-base-parent GitEE·地址 https://gitee.com/cicadasmile/java-base-parent

閱讀標簽

【Java基礎】【設計模式】【結構與算法】【Linux系統】【數據庫】

【分布式架構】【微服務】【大數據組件】【SpringBoot進階】【Spring&Boot基礎】

【數據分析】【技術導圖】【 職場】

總結

以上是生活随笔為你收集整理的Java容器 | 基于源码分析Map集合体系的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。