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

Java Map 原理、實(shí)現(xiàn)與使用場景分析

 更新時間:2025年12月23日 09:24:41   作者:heartbeat..  
文章詳細(xì)介紹了Java中的Map接口及其主要實(shí)現(xiàn)類,包括HashMap、LinkedHashMap、TreeMap、ConcurrentHashMap和Properties,涵蓋了Map的原理、核心方法、常見使用場景、遍歷方式以及注意事項(xiàng),感興趣的朋友跟隨小編一起看看吧

Java Map 詳解:原理、實(shí)現(xiàn)與使用場景

一、介紹

Map 是 Java 集合框架(java.util)中鍵值對(Key-Value) 形式的集合接口,與 List/Set 并列(繼承自 Collection 的父接口 Iterable,但不直接繼承 Collection)。核心特征是:鍵(Key)唯一且無序(部分實(shí)現(xiàn)有序),值(Value)可重復(fù),通過鍵快速查找值,是日常開發(fā)中存儲關(guān)聯(lián)數(shù)據(jù)的核心工具。

特性:

  1. 鍵值對映射:每個 Key 對應(yīng)唯一的 Value,Key 不允許重復(fù)(若重復(fù)插入,新 Value 會覆蓋舊 Value);
  2. Key 不可變性:作為 Key 的對象需重寫 hashCode()equals() 方法(保證哈希一致性),且建議使用不可變類型(如 String、Integer),避免修改后哈希值變化導(dǎo)致無法查找;
  3. 支持 null:不同實(shí)現(xiàn)類對 null 的支持不同(如 HashMap 允許 1 個 null Key + 多個 null Value,Hashtable 不允許 null);
  4. 無序性(默認(rèn)):大部分 Map 實(shí)現(xiàn)(如 HashMap)不保證鍵值對的存儲 / 遍歷順序,有序?qū)崿F(xiàn)需顯式指定(如 TreeMap、LinkedHashMap)。

二、Map 接口核心方法

Map 定義了鍵值對操作的核心方法,覆蓋增刪改查、遍歷等場景:

方法作用
V put(K key, V value)添加 / 替換鍵值對:Key 不存在則新增,存在則替換 Value,返回舊 Value(無則返回 null)
V get(Object key)根據(jù) Key 獲取 Value,Key 不存在返回 null
V remove(Object key)刪除指定 Key 的鍵值對,返回被刪除的 Value(無則返回 null)
boolean containsKey(Object key)判斷是否包含指定 Key
boolean containsValue(Object value)判斷是否包含指定 Value(需遍歷,效率低)
int size()返回鍵值對數(shù)量
boolean isEmpty()判斷是否為空
void clear()清空所有鍵值對
Set<K> keySet()返回所有 Key 的 Set 集合(視圖,修改會同步到原 Map)
Collection<V> values()返回所有 Value 的 Collection 集合(視圖)
Set<Map.Entry<K,V>> entrySet()返回鍵值對(Entry)的 Set 集合(遍歷最優(yōu)方式)

三、Map 主要實(shí)現(xiàn)類

Java 提供了多款 Map 實(shí)現(xiàn)類,適配不同的性能、有序性、并發(fā)場景,核心如下:

1. HashMap(最常用)

底層實(shí)現(xiàn):JDK 8 前 = 數(shù)組(哈希桶)+ 鏈表;JDK 8 后 = 數(shù)組 + 鏈表 / 紅黑樹(鏈表長度 ≥8 轉(zhuǎn)紅黑樹,≤6 轉(zhuǎn)回鏈表)。

補(bǔ)充:

HashMap 的底層由「哈希桶數(shù)組」+「鏈表 / 紅黑樹」組成,核心設(shè)計(jì)目標(biāo)是:通過哈希算法將 Key 映射到數(shù)組下標(biāo),實(shí)現(xiàn) O (1) 級別的快速存取;通過鏈表 / 紅黑樹解決「哈希沖突」(不同 Key 哈希值相同的情況)。

  • 哈希桶數(shù)組(table):默認(rèn)初始長度 16(2 的冪),每個下標(biāo)對應(yīng)一個「桶」,桶內(nèi)存儲鏈表或紅黑樹;
  • 鏈表:當(dāng)多個 Key 哈希到同一桶時,先以鏈表形式存儲;
  • 紅黑樹:當(dāng)鏈表長度 ≥8 且數(shù)組長度 ≥64 時,鏈表轉(zhuǎn)為紅黑樹(查詢性能從 O (n) 優(yōu)化為 O (log n));當(dāng)紅黑樹節(jié)點(diǎn)數(shù) ≤6 時,轉(zhuǎn)回鏈表(減少紅黑樹維護(hù)開銷)。

紅黑樹:

紅黑樹是帶紅 / 黑顏色標(biāo)記的自平衡二叉查找樹,也是二叉樹的一種,不過有5條規(guī)則對樹的高度進(jìn)行限制,增刪改查的時間復(fù)雜度穩(wěn)定在O(logn)

補(bǔ)充一下那5條規(guī)則:

(1)每個節(jié)點(diǎn)只能為黑色或者紅色

(2)根節(jié)點(diǎn)必須為黑色

(3)所有“空葉子節(jié)點(diǎn)”都是黑色

(4)紅色節(jié)點(diǎn)的父節(jié)點(diǎn)和子節(jié)點(diǎn)都必須為黑色(你也可以這樣理解,不能連續(xù)倆紅色節(jié)點(diǎn))

(5)從任意節(jié)點(diǎn)到它自己所有葉子節(jié)點(diǎn)(在紅黑樹中這個葉子節(jié)點(diǎn)就是指的空葉子節(jié)點(diǎn))的路徑中,黑色節(jié)點(diǎn)的數(shù)量完全相同

規(guī)則是為了做什么?

保證樹的結(jié)構(gòu)不會退化成鏈表,同時保證增刪查改始終是 O (log n)

(補(bǔ):最長路徑的長度 ≤ 2 × 最短路徑的長度,兩者的差值最大為 最短路徑的長度)

什么是桶?

桶就是 HashMap 哈希桶數(shù)組里的一個存儲單元,負(fù)責(zé)接收通過哈希計(jì)算分配過來的鍵值對節(jié)點(diǎn),解決哈希沖突的鏈表 / 紅黑樹也都是在桶內(nèi)部形成的。

簡單舉個例子:

把 HashMap 想象成一個 帶編號的快遞柜,這個快遞柜的每個獨(dú)立格子就是一個 。

  1. 快遞柜 = 哈希桶數(shù)組(table)快遞柜有很多個格子,每個格子都有自己的編號(比如 0、1、2…15),對應(yīng) HashMap 數(shù)組的索引。
  2. 桶 = 快遞柜的單個格子每個格子(桶)的作用是存放 “屬于自己” 的快遞。在 HashMap 中,通過哈希計(jì)算,會把鍵值對節(jié)點(diǎn)分配到對應(yīng)編號的桶里。
  3. 哈希沖突 = 一個格子里放多個快遞
    • 理想情況:一個快遞(節(jié)點(diǎn))對應(yīng)一個格子(桶),取件時直接按編號找,速度飛快(對應(yīng) HashMap 的 O (1) 存?。?/li>
    • 現(xiàn)實(shí)情況:可能出現(xiàn)多個快遞被分配到同一個格子(不同 Key 計(jì)算出相同的數(shù)組索引),這就是哈希沖突。這時,HashMap 會在這個桶里用鏈表把這些節(jié)點(diǎn)串起來;如果鏈表太長(≥8),就會變成紅黑樹,提升查詢效率。

核心特點(diǎn):

  • 存取效率高(平均 O (1) 時間復(fù)雜度),基于哈希表快速定位;
  • 無序(存儲順序 ≠ 插入順序);
  • 允許 1 個 null Key、多個 null Value;
  • 非線程安全(多線程操作可能導(dǎo)致死循環(huán)、數(shù)據(jù)丟失);
  • 初始容量 16,負(fù)載因子 0.75(擴(kuò)容閾值 = 容量 × 負(fù)載因子,默認(rèn) 12),擴(kuò)容時容量翻倍,且重新哈希。

機(jī)制詳解:

哈希計(jì)算與索引定位:

HashMap 高效存取的核心是「將 Key 映射到數(shù)組下標(biāo)」,分為兩步:

1.Key 的哈希值計(jì)算

為了減少哈希沖突,HashMap 對 Key 的 hashCode() 做了二次哈希(擾動函數(shù)):

static final int hash(Object key) {
    int h;
    // 核心邏輯:key.hashCode() ^ (h >>> 16)
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  • 作用:將 Key 的哈希值高 16 位與低 16 位異或,混合高低位特征,減少哈希沖突(尤其數(shù)組長度較小時,僅低幾位參與取模,混合后能利用高位信息);
  • null Key 處理:hash 值固定為 0,因此 null Key 始終映射到數(shù)組索引 0。

2.數(shù)組索引計(jì)算

通過哈希值計(jì)算數(shù)組下標(biāo),利用「位運(yùn)算替代取?!固嵘阅埽▋H當(dāng)數(shù)組長度為 2 的冪時生效):

// i = 哈希值 & (數(shù)組長度 - 1)
int index = hash & (table.length - 1);
  • 示例:數(shù)組長度 16(二進(jìn)制 10000),table.length - 1 = 15(二進(jìn)制 01111);哈希值與 15 按位與,等價于 hash % 16,但位運(yùn)算更快。
  • 為什么數(shù)組長度是 2 的冪?
  • → 保證hash & (length-1)等價于取模,且位運(yùn)算效率遠(yuǎn)高于取模;
  • → 擴(kuò)容時,節(jié)點(diǎn)新索引要么不變,要么 = 原索引 + 原數(shù)組長度(簡化擴(kuò)容遷移邏輯)。

擴(kuò)容機(jī)制:

條件:

  • 首次 put 時,table 為 null,初始化數(shù)組(默認(rèn)長度 16,threshold=16×0.75=12);
  • 后續(xù) put 時,若 size > threshold,觸發(fā)擴(kuò)容;
  • 鏈表長度≥8 但數(shù)組長度 < 64 時,優(yōu)先擴(kuò)容(而非樹化)。

步驟:

// 1. 計(jì)算新容量:原容量翻倍(始終保持2的冪)
int newCap = oldCap << 1; // 如16→32,32→64...
// 2. 計(jì)算新擴(kuò)容閾值:newThr = newCap × loadFactor
int newThr = oldThr << 1;
// 3. 創(chuàng)建新數(shù)組(長度newCap)
Node<K,V>[] newTab = (Node<K,V>[]) new Node[newCap];
table = newTab;
// 4. 遷移原數(shù)組節(jié)點(diǎn)到新數(shù)組
for (int j = 0; j < oldCap; ++j) {
    Node<K,V> e;
    if ((e = table[j]) != null) {
        table[j] = null; // 釋放原數(shù)組引用(幫助GC)
        // 情況1:桶內(nèi)僅一個節(jié)點(diǎn),直接計(jì)算新索引并放入
        if (e.next == null)
            newTab[e.hash & (newCap - 1)] = e;
        // 情況2:桶內(nèi)是紅黑樹,拆分紅黑樹并遷移
        else if (e instanceof TreeNode)
            ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
        // 情況3:桶內(nèi)是鏈表,拆分鏈表(優(yōu)化:無需重新哈希,僅判斷高位)
        else {
            // 鏈表拆分規(guī)則:新索引 = 原索引 或 原索引 + oldCap
            Node<K,V> loHead = null, loTail = null; // 原索引節(jié)點(diǎn)
            Node<K,V> hiHead = null, hiTail = null; // 原索引+oldCap節(jié)點(diǎn)
            Node<K,V> next;
            do {
                next = e.next;
                // 核心判斷:哈希值的第n位(oldCap的二進(jìn)制位)是否為0
                if ((e.hash & oldCap) == 0) {
                    // 第n位為0 → 新索引=原索引
                    if (loTail == null) loHead = e;
                    else loTail.next = e;
                    loTail = e;
                } else {
                    // 第n位為1 → 新索引=原索引+oldCap
                    if (hiTail == null) hiHead = e;
                    else hiTail.next = e;
                    hiTail = e;
                }
            } while ((e = next) != null);
            // 放入新數(shù)組
            if (loTail != null) {
                loTail.next = null;
                newTab[j] = loHead;
            }
            if (hiTail != null) {
                hiTail.next = null;
                newTab[j + oldCap] = hiHead;
            }
        }
    }
}

使用示例

Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Java", 1);
hashMap.put("Python", 2);
Integer value = hashMap.get("Java"); // 獲取值:1
hashMap.put("Java", 3); // 替換值,舊值 1 被覆蓋

各操作邏輯:

1.存

先定位桶 → 無沖突則新增節(jié)點(diǎn) → 有沖突則按鏈表 / 紅黑樹處理 → 替換重復(fù) Key 的 Value → 檢查擴(kuò)容。

2.取

定位桶 → 桶頭匹配直接返回 → 紅黑樹 / 鏈表遍歷匹配 → 未找到返回 null。

3.刪

定位待刪除節(jié)點(diǎn) → 按鏈表 / 紅黑樹規(guī)則刪除 → 更新 size 和 modCount。

2. LinkedHashMap

底層實(shí)現(xiàn):HashMap + 雙向鏈表(維護(hù)插入 / 訪問順序)。

補(bǔ)充:

LinkedHashMap 完全復(fù)用 HashMap 的哈希桶數(shù)組、節(jié)點(diǎn)哈希 / 擴(kuò)容 / 樹化等邏輯,僅通過「雙向鏈表」改造節(jié)點(diǎn)結(jié)構(gòu)、新增頭尾指針,實(shí)現(xiàn)有序性。

  • 哈希桶層:和 HashMap 完全一致,負(fù)責(zé)通過哈希快速定位節(jié)點(diǎn),解決哈希沖突(鏈表 / 紅黑樹);
  • 雙向鏈表層:所有節(jié)點(diǎn)通過 before/after 指針串聯(lián),head 指向最早節(jié)點(diǎn),tail 指向最新節(jié)點(diǎn),專門維護(hù)遍歷順序;
  • 節(jié)點(diǎn)復(fù)用:LinkedHashMap 的 Entry 繼承 HashMap.Node,因此哈希桶里的節(jié)點(diǎn)同時屬于「哈希鏈表 / 紅黑樹」和「雙向鏈表」,一個節(jié)點(diǎn)兩個 “身份”。

什么是節(jié)點(diǎn)?

在 LinkedHashMap(以及 HashMap)中,節(jié)點(diǎn)(Node/Entry) 是存儲「鍵值對(Key-Value)」的最小單元 —— 就像快遞包裹本身,而桶是放包裹的格子、雙向鏈表是串包裹的繩子。

舉個具體的例子:

比如你往 LinkedHashMap 里存 put("手機(jī)", 1999)、put("耳機(jī)", 299)put("充電器", 99)

  • 每個鍵值對對應(yīng)一個「節(jié)點(diǎn)」:
    • 節(jié)點(diǎn) 1:Key = 手機(jī),Value=1999;
    • 節(jié)點(diǎn) 2:Key = 耳機(jī),Value=299;
    • 節(jié)點(diǎn) 3:Key = 充電器,Value=99。
  • 這些節(jié)點(diǎn)會按哈希規(guī)則被分配到不同的桶(比如手機(jī)→3 號格、耳機(jī)→5 號格、充電器→3 號格):
  • 3 號桶里有兩個節(jié)點(diǎn)(手機(jī) + 充電器),用鏈表串起來;5 號桶只有耳機(jī)節(jié)點(diǎn)。
  • 同時,這三個節(jié)點(diǎn)會被雙向鏈表串成 head→手機(jī)→耳機(jī)→充電器→tail(插入順序);如果調(diào)用 get("手機(jī)"),節(jié)點(diǎn) 1 會被挪到尾部,鏈表變成 head→耳機(jī)→充電器→手機(jī)→tail(訪問順序)。

核心特點(diǎn):

  • 繼承 HashMap 的所有特性,額外保證「有序性」;
  • 有序模式:
    • 插入順序(默認(rèn)):遍歷順序 = 插入順序;
    • 訪問順序:調(diào)用 get()/put() 后,該鍵值對移至鏈表尾部(可實(shí)現(xiàn) LRU 緩存);
  • 非線程安全,性能略低于 HashMap(維護(hù)鏈表開銷)。

使用示例(LRU 緩存)

// 初始容量16,負(fù)載因子0.75,訪問順序模式
Map<String, Integer> lruMap = new LinkedHashMap<>(16, 0.75f, true);
lruMap.put("a", 1);
lruMap.put("b", 2);
lruMap.get("a"); // 訪問后,"a" 移至尾部
// 遍歷順序:b → a(訪問順序)
for (Map.Entry<String, Integer> entry : lruMap.entrySet()) {
    System.out.println(entry.getKey());
}

3. TreeMap

底層實(shí)現(xiàn):紅黑樹(自平衡的二叉查找樹)。

核心特點(diǎn):

  • 有序:默認(rèn)按 Key 的自然順序(如 Integer 升序、String 字典序),或自定義 Comparator 排序;
  • 無 null Key(會拋 NullPointerException),允許 null Value;
  • 存取效率 O (log n)(紅黑樹查找 / 插入),低于 HashMap;
  • 非線程安全;
  • 適合需要排序的場景(如按 Key 范圍查詢)。

使用示例(自定義排序)

// 按 Key 降序排序
Map<Integer, String> treeMap = new TreeMap<>((k1, k2) -> k2 - k1);
treeMap.put(3, "c");
treeMap.put(1, "a");
treeMap.put(2, "b");
// 遍歷順序:3 → 2 → 1
for (Integer key : treeMap.keySet()) {
    System.out.println(key);
}

4. Hashtable(古老的線程安全實(shí)現(xiàn))

底層實(shí)現(xiàn):數(shù)組 + 鏈表(JDK 8 未優(yōu)化紅黑樹)。

補(bǔ)充:

Hashtable 無紅黑樹優(yōu)化(全程僅數(shù)組 + 鏈表),線程安全通過「方法級 synchronized」實(shí)現(xiàn),是典型的 “簡單粗暴” 的線程安全哈希表設(shè)計(jì)。

  • 哈希桶數(shù)組:默認(rèn)初始長度 11(非 2 的冪,區(qū)別于 HashMap),每個下標(biāo)對應(yīng)一個桶,桶內(nèi)存儲鏈表;
  • 鏈表:所有哈希沖突的節(jié)點(diǎn)(Key 哈希到同一桶)通過 next 指針串聯(lián),無紅黑樹優(yōu)化,鏈表過長時查詢性能退化至 O (n);
  • 線程安全:所有核心方法(put/get/remove)都加 synchronized 鎖,鎖粒度為整個 Hashtable 對象。

核心特點(diǎn)

  • 線程安全(方法級 synchronized 鎖),但鎖粒度大,性能差;
  • 不允許 null Key/Value(拋 NullPointerException);
  • 初始容量 11,負(fù)載因子 0.75,擴(kuò)容時容量 = 原容量 ×2 +1;
  • 基本被 ConcurrentHashMap 替代,僅兼容舊代碼時使用。

機(jī)制詳解:

哈希計(jì)算與擴(kuò)容:

Key 的哈希計(jì)算(無擾動優(yōu)化)

Hashtable 的哈希計(jì)算邏輯簡單,無 HashMap 的 “高低位異或” 擾動優(yōu)化,且直接使用 key.hashCode()(不允許 null Key):

// 計(jì)算Key的哈希值(簡化版)
int hash = key.hashCode();
// 計(jì)算數(shù)組索引(取模,而非位運(yùn)算)
int index = (hash & 0x7FFFFFFF) % table.length;

擴(kuò)容機(jī)制(區(qū)別于HashMap):

Hashtable 擴(kuò)容觸發(fā)后,新容量 = 原容量 × 2 + 1(而非 HashMap 的翻倍)

// 1. 計(jì)算新容量:原容量 ×2 +1(如 11→23,23→47...)
int newCapacity = table.length * 2 + 1;
// 2. 計(jì)算新擴(kuò)容閾值:newThr = (int)(newCapacity × loadFactor)
int newThreshold = (int)(newCapacity * loadFactor);
// 3. 創(chuàng)建新數(shù)組(長度newCapacity)
Entry<?,?>[] newTable = new Entry<?,?>[newCapacity];
// 4. 遷移原數(shù)組節(jié)點(diǎn)到新數(shù)組(重新計(jì)算索引,拷貝鏈表)
for (int i = 0; i < table.length; i++) {
    Entry<K,V> e = (Entry<K,V>)table[i];
    if (e != null) {
        table[i] = null; // 釋放原數(shù)組引用
        do {
            Entry<K,V> next = e.next;
            // 重新計(jì)算新索引(取模)
            int index = (e.hash & 0x7FFFFFFF) % newCapacity;
            e.next = (Entry<K,V>)newTable[index];
            newTable[index] = e;
            e = next;
        } while (e != null);
    }
}
// 5. 替換數(shù)組和閾值
table = newTable;
threshold = newThreshold;

差異總結(jié):

容量增長規(guī)則:2n+1(保證素?cái)?shù),理論減少哈希沖突,但無實(shí)際性能優(yōu)勢),HashMap 是 2n(2 的冪,位運(yùn)算優(yōu)化);

索引重計(jì)算:每次擴(kuò)容都需對所有節(jié)點(diǎn)重新取模,無 HashMap 的 “高位判斷” 優(yōu)化,擴(kuò)容開銷更大;

  • 觸發(fā)條件:count ≥ threshold(HashMap 是 size > threshold),更早觸發(fā)擴(kuò)容。

5. ConcurrentHashMap(并發(fā)安全)

底層實(shí)現(xiàn):JDK 8 前 = 分段鎖(Segment 數(shù)組 + HashMap);JDK 8 后 = 數(shù)組 + 鏈表 / 紅黑樹 + CAS + synchronized(鎖哈希桶,粒度更細(xì))。

補(bǔ)充:
  • 哈希桶層:與 HashMap 一致(數(shù)組長度為 2 的冪,鏈表≥8 轉(zhuǎn)紅黑樹),但數(shù)組 / 節(jié)點(diǎn)關(guān)鍵字段(table/val/next)用 volatile 保證多線程可見性;
  • 并發(fā)控制層:
    • 無競爭時:用 CAS 操作新增節(jié)點(diǎn)(無鎖);
    • 有競爭時:對「單個哈希桶」加 synchronized 鎖(僅鎖住沖突的桶,其他桶可并行操作);
    • 擴(kuò)容時:用 ForwardingNode 標(biāo)記桶狀態(tài),多線程協(xié)同擴(kuò)容(避免單線程擴(kuò)容瓶頸);
  • 節(jié)點(diǎn)特性:Node 節(jié)點(diǎn)的 val/nextvolatile,保證修改后其他線程能立即感知。

核心特點(diǎn):

  • 線程安全(高并發(fā)):JDK 8 后鎖粒度為「單個哈希桶」,并發(fā)性能遠(yuǎn)高于 Hashtable;
  • 支持 1 個 null Key(JDK 8 后)、多個 null Value;
  • 存取效率接近 HashMap(并發(fā)場景下);
  • 迭代器是 “弱一致性”,不會拋 ConcurrentModificationException;
  • 適合高并發(fā)讀寫的場景(如分布式緩存、業(yè)務(wù)核心數(shù)據(jù)存儲)。

擴(kuò)容機(jī)制(多線程協(xié)同)

當(dāng) size > 容量 × 負(fù)載因子(默認(rèn)初始容量 16,負(fù)載因子 0.75,閾值 12),且當(dāng)前數(shù)組(table)無其他擴(kuò)容操作時觸發(fā)。

ConcurrentHashMap 擴(kuò)容由「觸發(fā)線程 + 其他訪問線程」協(xié)同完成,避免單線程擴(kuò)容瓶頸:

  1. 觸發(fā)條件:size > sizeCtl(同 HashMap);
  2. 擴(kuò)容流程:
    • 創(chuàng)建 nextTable(容量翻倍);
    • 遍歷 table 桶,將桶節(jié)點(diǎn)遷移到 nextTable;
    • 遷移時,將原桶頭節(jié)點(diǎn)替換為 ForwardingNode(標(biāo)記擴(kuò)容中);
    • 其他線程訪問該桶時,檢測到 ForwardingNode,會暫停當(dāng)前操作,協(xié)助擴(kuò)容;
  3. 核心優(yōu)勢:多線程并行遷移不同桶,大幅提升擴(kuò)容效率。

使用示例

Map<String, String> concurrentMap = new ConcurrentHashMap<>();
// 多線程安全寫入
new Thread(() -> concurrentMap.put("thread1", "data1")).start();
new Thread(() -> concurrentMap.put("thread2", "data2")).start();
// 安全讀取
System.out.println(concurrentMap.get("thread1"));

6. Properties(配置文件專用)

底層實(shí)現(xiàn):繼承 Hashtable,Key/Value 均為 String 類型。

補(bǔ)充:

Properties 完全復(fù)用 Hashtable 的哈希表核心邏輯(數(shù)組 + 鏈表、方法級 synchronized 鎖、禁止 null 值),僅在其基礎(chǔ)上增加「Key/Value 強(qiáng)制為 String 類型」和「配置文件讀寫」的擴(kuò)展方法。

核心存儲層:完全復(fù)用 Hashtable 的 table 數(shù)組(初始容量 11,擴(kuò)容規(guī)則 2n+1)、Entry 鏈表節(jié)點(diǎn),線程安全通過方法級 synchronized 實(shí)現(xiàn);

擴(kuò)展層:

  • 類型約束:Key/Value 實(shí)際使用時強(qiáng)制為 String(雖底層仍存儲 Object,但 setProperty()/getProperty() 限定為 String);
  • 配置繼承:通過 defaults 實(shí)現(xiàn)配置復(fù)用(當(dāng)前配置無 Key 時,從默認(rèn)配置集查找);
  • IO 能力:load()/store() 方法解析 / 生成 .properties 格式的配置文件(鍵值對以 key=value 換行存儲)。

核心特點(diǎn):

  • 專為讀取配置文件(.properties)設(shè)計(jì),支持 load()/store() 方法讀寫文件;
  • 線程安全(繼承 Hashtable 的 synchronized 方法);
  • 常用場景:讀取系統(tǒng)配置、項(xiàng)目配置文件。

操作原理:

配置文件加載(load ())

load() 是 Properties 最核心的擴(kuò)展方法,用于從 .properties 文件 / 輸入流解析鍵值對:

// 示例:加載配置文件
Properties props = new Properties();
props.load(new FileInputStream("config.properties"));

解析:

  1. 按行讀取輸入流,忽略注釋行(以 #/! 開頭)和空行;
  2. 解析每行的 key=value 格式(支持 :/ 空格 作為分隔符),去除首尾空格;
  3. 調(diào)用 put(key, value)(繼承 Hashtable)將鍵值對存入哈希表;
  4. 全程加 synchronized 鎖,保證線程安全。

配置獲取(getProperty ())

getProperty() 是 String 專屬的獲取方法,支持默認(rèn)配置集查找:

String url = props.getProperty("db.url");
// 帶默認(rèn)值:若Key不存在,返回默認(rèn)值"jdbc:mysql://localhost"
String url = props.getProperty("db.url", "jdbc:mysql://localhost");

查找邏輯:

  1. 調(diào)用 Hashtable 的 get(key) 獲取值,強(qiáng)轉(zhuǎn)為 String;
  2. 若值為 null 且 defaults 不為 null,遞歸從 defaults 中查找;
  3. 若仍為 null,返回指定的默認(rèn)值(或 null)。

配置存儲(store ())

store() 用于將配置寫入文件 / 輸出流,生成標(biāo)準(zhǔn) .properties 文件:

props.store(new FileOutputStream("config.properties"), "DB Config");

寫入邏輯:

  1. 先寫入注釋(可選)和當(dāng)前時間戳;
  2. 遍歷哈希表的 Entry 鏈表,按 key=value 格式逐行寫入;
  3. 全程加 synchronized 鎖,保證寫入過程不被打斷。

基礎(chǔ)操作(put/remove)

Properties 未重寫 Hashtable 的 put()/remove() 方法,完全復(fù)用其邏輯:

  • setProperty(key, value) 本質(zhì)是調(diào)用 put(key, value),僅限定參數(shù)為 String;
  • 所有操作均加 synchronized 鎖,線程安全但并發(fā)性能差(同 Hashtable)。

使用示例

Properties props = new Properties();
// 讀取配置文件
props.load(new FileInputStream("config.properties"));
String url = props.getProperty("db.url"); // 獲取配置值

四、Map 常見使用場景

場景推薦實(shí)現(xiàn)類
日常開發(fā)、無排序、高讀寫性能HashMap
需要有序(插入 / 訪問順序)、LRU 緩存LinkedHashMap
需要按 Key 排序、范圍查詢TreeMap
高并發(fā)讀寫、線程安全ConcurrentHashMap
配置文件讀寫Properties
舊代碼兼容、低并發(fā)線程安全Hashtable

五、Map 遍歷方式(性能對比)

Map 遍歷的核心是操作 entrySet()/keySet()/values(),推薦優(yōu)先級:

entrySet 遍歷(最優(yōu)):直接獲取鍵值對,無需二次查詢,性能最高;

for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
}

迭代器遍歷(支持刪除):適合遍歷中刪除元素,避免 ConcurrentModificationException;

Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<String, Integer> entry = it.next();
    if (entry.getValue() == 2) {
        it.remove(); // 安全刪除
    }
}

keySet + get () 遍歷(低效):需二次哈希查詢,性能最差,不推薦;

for (String key : map.keySet()) {
    Integer value = map.get(key); // 額外哈希查詢
}

Lambda 遍歷(簡潔):JDK 8+ 支持,代碼簡潔,性能接近 entrySet;

map.forEach((key, value) -> System.out.println(key + ":" + value));

六、注意事項(xiàng)

  1. Key 的哈希與相等性:
    • 自定義對象作為 Key 時,必須重寫 hashCode()equals()(保證相同對象哈希值相同,不同對象哈希值盡量不同);
    • 避免使用可變對象作為 Key(如 ArrayList),修改后哈希值變化會導(dǎo)致無法查找。
  2. HashMap 擴(kuò)容優(yōu)化:
    • 已知元素?cái)?shù)量時,提前指定初始容量(如 new HashMap<>(100)),避免頻繁擴(kuò)容(擴(kuò)容需重新哈希,開銷大);
    • 負(fù)載因子默認(rèn) 0.75 是性能與內(nèi)存的平衡,無需輕易修改。
  3. 并發(fā)安全:
    • HashMap/LinkedHashMap/TreeMap 非線程安全,多線程修改需手動加鎖(如 Collections.synchronizedMap()),或直接使用 ConcurrentHashMap;
    • ConcurrentHashMap 不支持 null Key(JDK 7)/ 支持 1 個 null Key(JDK 8+),需注意空值處理。
  4. TreeMap 排序:
    • 自定義對象作為 Key 時,需實(shí)現(xiàn) Comparable 接口,或創(chuàng)建 TreeMap 時指定 Comparator,否則拋 ClassCastException

七、總結(jié)

Map 是 Java 中存儲鍵值對的核心集合,核心實(shí)現(xiàn)類各有側(cè)重:

  • HashMap:性能均衡,適合絕大多數(shù)無排序、非并發(fā)場景;
  • LinkedHashMap:有序 + HashMap 特性,適合緩存場景;
  • TreeMap:排序 + 范圍查詢,適合有序鍵值對場景;
  • ConcurrentHashMap:高并發(fā)安全,適合核心業(yè)務(wù)的并發(fā)存儲;
  • Properties:配置文件專用,簡單易用。

七、總結(jié)

Map 是 Java 中存儲鍵值對的核心集合,核心實(shí)現(xiàn)類各有側(cè)重:

  • HashMap:性能均衡,適合絕大多數(shù)無排序、非并發(fā)場景;
  • LinkedHashMap:有序 + HashMap 特性,適合緩存場景;
  • TreeMap:排序 + 范圍查詢,適合有序鍵值對場景;
  • ConcurrentHashMap:高并發(fā)安全,適合核心業(yè)務(wù)的并發(fā)存儲;
  • Properties:配置文件專用,簡單易用。

選擇時需根據(jù)「有序性、并發(fā)要求、性能、業(yè)務(wù)場景」四大維度權(quán)衡,同時注意 Key 的不可變性和遍歷方式的性能優(yōu)化。

附表:

JDK 版本核心變化點(diǎn)涉及 Map 實(shí)現(xiàn)類詳細(xì)說明
JDK 1.0初始版本發(fā)布Hashtable1. 引入 Hashtable,底層為「數(shù)組 + 鏈表」,方法級 synchronized 保證線程安全;2. 不支持 null Key/Value,初始容量 11,擴(kuò)容規(guī)則 2n+1;3. 無紅黑樹、無擾動哈希、無并發(fā)優(yōu)化。
JDK 1.2集合框架重構(gòu)HashMap、TreeMap1. 引入 HashMap,替代 Hashtable 成為非線程安全哈希表首選;- 底層「數(shù)組 + 鏈表」,初始容量 16(2 的冪),負(fù)載因子 0.75;- 支持 1 個 null Key、多個 null Value;- 實(shí)現(xiàn)擾動哈希(高低位異或)、位運(yùn)算索引計(jì)算;2. 引入 TreeMap,底層紅黑樹,支持 Key 自然排序 / 自定義排序,無 null Key;3. 引入 LinkedHashMap(HashMap 子類),雙向鏈表維護(hù)插入 / 訪問順序。
JDK 1.4配置增強(qiáng)Properties1. 基于 Hashtable 擴(kuò)展,強(qiáng)化 .properties 配置文件讀寫能力;2. 新增 loadFromXML()/storeToXML() 方法,支持 XML 格式配置。
JDK 5.0泛型支持所有 Map 實(shí)現(xiàn)類1. 引入泛型(Map<K,V>),替代原始 Object 類型,避免強(qiáng)制類型轉(zhuǎn)換;2. 優(yōu)化 TreeMap 比較器,支持 Comparator<? super K> 泛型約束。
JDK 6.0性能微調(diào)HashMap、Hashtable1. 優(yōu)化 HashMap 哈希算法,減少哈希沖突概率;2. 調(diào)整 Hashtable 擴(kuò)容閾值計(jì)算邏輯,提升低容量場景性能。
JDK 7.0并發(fā)優(yōu)化ConcurrentHashMap1. 重寫 ConcurrentHashMap,底層「分段鎖(Segment)+ HashMap」;- 分段鎖粒度為 Segment(默認(rèn) 16 個),并發(fā)度遠(yuǎn)高于 Hashtable;- 不支持 null Key/Value,避免并發(fā)場景空指針歧義;2. HashMap 優(yōu)化擴(kuò)容邏輯,減少擴(kuò)容時的哈希沖突。
JDK 8.0核心重構(gòu)(里程碑)HashMap、ConcurrentHashMap、LinkedHashMap1. HashMap 重大升級:- 鏈表長度 ≥8 且數(shù)組長度 ≥64 時,鏈表轉(zhuǎn)紅黑樹(查詢從 O (n)→O (log n));- 紅黑樹節(jié)點(diǎn)數(shù) ≤6 時,轉(zhuǎn)回鏈表(降低維護(hù)開銷);- 優(yōu)化擴(kuò)容機(jī)制,多線程擴(kuò)容(但仍非線程安全);- 簡化擾動哈希邏輯(key.hashCode() ^ (h >>> 16));2. ConcurrentHashMap 重構(gòu):- 廢棄分段鎖,改用「CAS + synchronized 鎖單個哈希桶」,并發(fā)粒度更細(xì);- 支持 1 個 null Key(區(qū)別于 JDK 7),兼容 HashMap 用法;- 引入紅黑樹優(yōu)化(同 HashMap);3. LinkedHashMap 優(yōu)化:- 強(qiáng)化 LRU 場景性能,訪問順序模式下節(jié)點(diǎn)遷移效率提升;4. 新增 Lambda 遍歷(forEach() 方法),簡化遍歷寫法。
JDK 9.0工廠方法增強(qiáng)所有 Map 實(shí)現(xiàn)類1. 引入不可變 Map 工廠方法:- Map.of()(最多 10 個鍵值對)、Map.ofEntries()(不限數(shù)量);- 不可變 Map 不支持增刪改,無 null Key/Value,性能更高。
JDK 11.0性能與安全優(yōu)化ConcurrentHashMap、HashMap1. 優(yōu)化 ConcurrentHashMap 擴(kuò)容邏輯,多線程協(xié)同擴(kuò)容效率提升;2. 修復(fù) HashMap 極端哈希值下的性能退化問題;3. 增強(qiáng)不可變 Map 的序列化安全性。
JDK 17.0長期支持版優(yōu)化所有 Map 實(shí)現(xiàn)類1. 穩(wěn)定化不可變 Map 實(shí)現(xiàn),修復(fù)多線程下的弱一致性問題;2. 優(yōu)化 TreeMap 紅黑樹旋轉(zhuǎn)邏輯,減少排序耗時;3. 廢棄 Hashtable 部分過時方法(如 elements()),推薦 ConcurrentHashMap 替代。

到此這篇關(guān)于Java Map 原理、實(shí)現(xiàn)與使用場景分析的文章就介紹到這了,更多相關(guān)Java Map 使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot自動裝配原理解析

    SpringBoot自動裝配原理解析

    Spring Boot自動裝配是指在Spring Boot應(yīng)用啟動時,根據(jù)類路徑下的jar包依賴、Bean定義、各種配置文件等信息,自動配置Spring應(yīng)用上下文的Bean,本文給大家詳細(xì)解析了SpringBoot自動裝配原理,需要的朋友可以參考下
    2024-11-11
  • JAVA使用反射讀取注解方式

    JAVA使用反射讀取注解方式

    在Java中,反射機(jī)制不僅可以操作對象的屬性和方法,還可以讀取注解,本文介紹了反射讀取注解的步驟,包括定義注解、使用注解、通過反射獲取注解信息及處理注解信息,通過具體代碼例子和運(yùn)行結(jié)果,詳細(xì)解釋了注解的定義、使用和反射獲取注解的過程
    2024-09-09
  • Spring Batch遠(yuǎn)程分區(qū)的本地Jar包模式的代碼詳解

    Spring Batch遠(yuǎn)程分區(qū)的本地Jar包模式的代碼詳解

    這篇文章主要介紹了Spring Batch遠(yuǎn)程分區(qū)的本地Jar包模式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Spring?Boot條件注解之@ConditionalOnProperty完全解析

    Spring?Boot條件注解之@ConditionalOnProperty完全解析

    這篇文章主要介紹了SpringBoot中的@ConditionalOnProperty注解,通過配置文件屬性值控制Bean或配置類的加載,實(shí)現(xiàn)功能開關(guān)和環(huán)境配置,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • java獲取網(wǎng)絡(luò)圖片上傳到OSS的方法

    java獲取網(wǎng)絡(luò)圖片上傳到OSS的方法

    這篇文章主要為大家詳細(xì)介紹了java獲取網(wǎng)絡(luò)圖片上傳到OSS,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • 詳解Jmeter線程組的設(shè)置方法

    詳解Jmeter線程組的設(shè)置方法

    本文主要介紹了Jmeter線程組的設(shè)置方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • SpringBoot實(shí)現(xiàn)簡單文件上傳功能

    SpringBoot實(shí)現(xiàn)簡單文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)簡單文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 簡單談?wù)凾hreadPoolExecutor線程池之submit方法

    簡單談?wù)凾hreadPoolExecutor線程池之submit方法

    下面小編就為大家?guī)硪黄唵握務(wù)凾hreadPoolExecutor線程池之submit方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Springboot整合fastdfs實(shí)現(xiàn)分布式文件存儲

    Springboot整合fastdfs實(shí)現(xiàn)分布式文件存儲

    本文主要介紹了Springboot整合fastdfs實(shí)現(xiàn)分布式文件存儲,詳細(xì)闡述了Springboot應(yīng)用程序如何與FastDFS進(jìn)行集成及演示了如何使用Springboot和FastDFS實(shí)現(xiàn)分布式文件存儲,感興趣的可以了解一下
    2023-08-08
  • 基于String變量的兩種創(chuàng)建方式(詳解)

    基于String變量的兩種創(chuàng)建方式(詳解)

    下面小編就為大家?guī)硪黄赟tring變量的兩種創(chuàng)建方式(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07

最新評論

日土县| 泊头市| 宁都县| 喜德县| 洪洞县| 长垣县| 喀喇| 出国| 富阳市| 济南市| 巴塘县| 水富县| 加查县| 泸水县| 杂多县| 庄浪县| 健康| 横山县| 苗栗市| 乌苏市| 萨迦县| 濮阳市| 鸡泽县| 和政县| 铜梁县| 镇平县| 沙河市| 六枝特区| 民和| 韶关市| 嘉兴市| 仲巴县| 阜南县| 九龙坡区| 淮滨县| 西藏| 潢川县| 高清| 廊坊市| 旌德县| 凤庆县|