亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JDK8 HashMap紅黑樹退化為鏈表的機(jī)制方式

 更新時(shí)間:2025年05月13日 09:58:48   作者:找不到、了  
這篇文章主要介紹了JDK8 HashMap紅黑樹退化為鏈表的機(jī)制方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

1、數(shù)據(jù)結(jié)構(gòu)

jdk8及之后,由hashmap由數(shù)組+鏈表(紅黑樹組成)。

如下圖所示:

桶數(shù)組是用來存儲數(shù)據(jù)元素,鏈表是用來解決沖突,紅黑樹是為了提高查詢的效率。

數(shù)據(jù)元素通過映射關(guān)系,也就是散列函數(shù),映射到桶數(shù)組對應(yīng)索引的位置。

如下圖所示:

如果發(fā)生沖突,從沖突的位置拉一個(gè)鏈表,插入沖突的元素。

如果鏈表長度>8&數(shù)組大小>=64,鏈表轉(zhuǎn)為紅黑樹。

如果紅黑樹節(jié)點(diǎn)個(gè)數(shù)<6 ,轉(zhuǎn)為鏈表。

2、Fail-Fast機(jī)制

Fail-Fast(快速失?。┦荍ava集合框架中一種重要的并發(fā)修改檢測機(jī)制,在HashMap中主要用于防止在迭代過程中集合被意外修改而導(dǎo)致數(shù)據(jù)不一致的問題。

2.1、核心作用

Fail-Fast機(jī)制就像集合的"安全警報(bào)系統(tǒng)":

  • 實(shí)時(shí)監(jiān)控:檢測迭代期間的意外修改
  • 快速響應(yīng):立即拋出ConcurrentModificationException
  • 預(yù)防損害:避免產(chǎn)生不可預(yù)知的錯(cuò)誤結(jié)果

2.2、實(shí)現(xiàn)原理

1. 關(guān)鍵變量

// HashMap中的修改計(jì)數(shù)器
transient int modCount;

// 迭代器中保存的計(jì)數(shù)器快照
int expectedModCount;

2. 工作流程

2.3、觸發(fā)場景

1. 迭代時(shí)修改集合

Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);

Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
    String key = it.next();
    map.put("C", 3);  // 這里會觸發(fā)fail-fast
}

2. 多線程并發(fā)修改

Map<Integer, String> map = new HashMap<>();
map.put(1, "One");

new Thread(() -> {
    map.put(2, "Two");  // 可能觸發(fā)主線程迭代時(shí)fail-fast
}).start();

for (Integer key : map.keySet()) {  // 可能拋出異常
    System.out.println(key);
}

2.4、實(shí)現(xiàn)細(xì)節(jié)

1. 修改計(jì)數(shù)更新點(diǎn)

// HashMap中的修改操作都會增加modCount
public V put(K key, V value) {
    // ...
    ++modCount;
    // ...
}

public V remove(Object key) {
    // ...
    ++modCount;
    // ...
}

public void clear() {
    // ...
    ++modCount;
    // ...
}

2. 迭代器檢查點(diǎn)

final class KeyIterator extends HashIterator 
    implements Iterator<K> {
    public final K next() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // ...
    }
}

2.5、對比

2.6、注意事項(xiàng)

1.正確刪除元素

// 錯(cuò)誤方式(觸發(fā)fail-fast)
for (String key : map.keySet()) {
    if (key.equals("remove")) {
        map.remove(key);  // 直接修改原集合
    }
}

// 正確方式(使用迭代器的remove)
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
    if (it.next().equals("remove")) {
        it.remove();  // 不會增加modCount
    }
}

2.多線程解決方案

  • 使用ConcurrentHashMap替代
  • 或者使用顯式同步:
synchronized(map) {
    for (String key : map.keySet()) {
        // 操作代碼
    }
}

3.性能監(jiān)控

// 檢測異常頻率
try {
    for (Entry<K,V> e : map.entrySet()) {
        // ...
    }
} catch (ConcurrentModificationException ex) {
    metrics.record("fail-fast.triggered");
}

Fail-Fast機(jī)制雖然會給開發(fā)者帶來一些"麻煩",但它有效地預(yù)防了更危險(xiǎn)的隱性數(shù)據(jù)一致性問題,是Java集合框架健壯性的重要保障。

理解這一機(jī)制可以幫助開發(fā)者寫出更安全的集合操作代碼。

3、核心結(jié)論

在JDK8+的HashMap中:

  • 確實(shí)存在紅黑樹退化為鏈表的機(jī)制(當(dāng)節(jié)點(diǎn)數(shù)≤6時(shí))
  • 這不是紅黑樹自身的特性,而是HashMap的主動優(yōu)化
  • 轉(zhuǎn)換是安全的,因?yàn)檫@是在擴(kuò)容(resize)或刪除(remove)時(shí)觸發(fā)的

關(guān)于樹化與退化閾值如下圖所示:

4、轉(zhuǎn)化安全機(jī)制

HashMap在JDK8引入的紅黑樹轉(zhuǎn)換機(jī)制包含嚴(yán)格的安全保障措施,確保在鏈表與紅黑樹相互轉(zhuǎn)換時(shí)不會破壞數(shù)據(jù)一致性和線程安全。

4.1. 觸發(fā)場景

1. 樹化(鏈表 → 紅黑樹)條件

使用treeifybin()方法。

// HashMap.treeifyBin() 片段
if (binCount >= TREEIFY_THRESHOLD - 1) { // TREEIFY_THRESHOLD=8
    if (tab.length < MIN_TREEIFY_CAPACITY) // MIN_TREEIFY_CAPACITY=64
        resize();
    else
        treeifyBin(tab, hash);
}

雙重校驗(yàn)保障

單鏈表長度≥8

哈希表容量≥64

  • 避免小表頻繁樹化
  • 確保有足夠分散的桶空間

2. 退化(紅黑樹 → 鏈表)條件

// HashMap.resize() 片段
if (lc <= UNTREEIFY_THRESHOLD)  // UNTREEIFY_THRESHOLD=6
    tab[index] = loHead.untreeify(map);

安全邊界

  • 樹節(jié)點(diǎn)≤6時(shí)才退化(比樹化閾值低2,避免頻繁轉(zhuǎn)換)

4.2. 轉(zhuǎn)換過程

如下圖所示:

1. 鏈表→紅黑樹轉(zhuǎn)換流程

關(guān)鍵保障

  • 持有桶頭節(jié)點(diǎn)鎖再進(jìn)行轉(zhuǎn)換
  • 新建TreeNode時(shí)保留原鏈表順序(通過next指針)
  • 平衡操作不改變元素哈希位置

2. 紅黑樹→鏈表轉(zhuǎn)換流程

如下圖所示:

代碼示例:

// TreeNode.untreeify() 實(shí)現(xiàn)
final Node<K,V> untreeify(HashMap<K,V> map) {
    Node<K,V> hd = null, tl = null;
    for (TreeNode<K,V> q = this; q != null; q = q.next) {
        Node<K,V> p = map.replacementNode(q, null); // 新建普通節(jié)點(diǎn)
        if (tl == null)
            hd = p;
        else
            tl.next = p;
        tl = p;
    }
    return hd;
}

安全保障

  • 按原有鏈表順序(通過TreeNode保留的next指針)重建
  • 新建普通節(jié)點(diǎn)而非修改原節(jié)點(diǎn),避免并發(fā)訪問問題
  • 轉(zhuǎn)換完成后原TreeNode可被GC回收

4.3. 并發(fā)安全機(jī)制

1、轉(zhuǎn)換期間不影響迭代器一致性

abstract class HashIterator {
    Node<K,V> next;        // 下一個(gè)返回的節(jié)點(diǎn)
    Node<K,V> current;     // 當(dāng)前節(jié)點(diǎn)
    int expectedModCount;  // 修改計(jì)數(shù)器快照
    
    final Node<K,V> nextNode() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // ...
    }
}

失效保護(hù)

  • 迭代期間檢測modCount變化
  • 快速失敗(fail-fast)機(jī)制

2、始終維持元素的原始存儲順序

1. 雙向鏈表維護(hù)

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent;  // 紅黑樹父節(jié)點(diǎn)
    TreeNode<K,V> left;    // 左子樹
    TreeNode<K,V> right;   // 右子樹
    TreeNode<K,V> prev;    // 鏈表前驅(qū)節(jié)點(diǎn)(刪除時(shí)需要)
    boolean red;
    // 仍然保留next指針(繼承自Entry)
}

雙重結(jié)構(gòu)

紅黑樹結(jié)構(gòu):parent/left/right

鏈表結(jié)構(gòu):next/prev

  • 保證在退化時(shí)可以快速重建鏈表
  • 支持按插入順序遍歷

2. 哈希值不變性

// TreeNode既保持hash值又維持鏈表順序
Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
    return new Node<>(p.hash, p.key, p.value, next);
}

轉(zhuǎn)換過程中始終保持:

  • 鍵的hashCode不變
  • 鍵對象的equals()不變
  • 值對象引用不變

3、線程安全(在持有鎖的情況下進(jìn)行)

4、異常處理機(jī)制(可進(jìn)行回滾)

1. 轉(zhuǎn)換失敗回滾

try {
    treeifyBin(tab, hash);
} catch (Throwable t) {
    tab[index] = originalHead; // 回退到原鏈表
    throw t;
}

2. 內(nèi)存溢出防護(hù)

// TreeNode構(gòu)造時(shí)檢查內(nèi)存
if (remaining < treeNodeSpace) {
    untreeify(); // 立即退化為鏈表
    return;
}

HashMap的轉(zhuǎn)換安全機(jī)制通過精細(xì)的鎖控制、結(jié)構(gòu)隔離和狀態(tài)校驗(yàn),在保證性能的同時(shí)實(shí)現(xiàn)了線程安全和數(shù)據(jù)一致性。

這種設(shè)計(jì)體現(xiàn)了Java集合框架在高并發(fā)場景下的工程智慧,也是為什么HashMap能成為最常用的數(shù)據(jù)結(jié)構(gòu)之一的關(guān)鍵所在。

5、設(shè)計(jì)原因

5.1. 性能權(quán)衡

數(shù)學(xué)驗(yàn)證

當(dāng)n=6時(shí):

  • 鏈表平均查找次數(shù):3次
  • 紅黑樹查找次數(shù):log?6≈2.58次
  • 性能差距不大,但紅黑樹維護(hù)成本更高

5.2. 空間局部性

  • 鏈表節(jié)點(diǎn)內(nèi)存連續(xù)訪問更友好
  • 紅黑樹的樹節(jié)點(diǎn)結(jié)構(gòu)更復(fù)雜:
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent;  // 父節(jié)點(diǎn)指針
    TreeNode<K,V> left;    // 左子樹指針
    TreeNode<K,V> right;   // 右子樹指針
    TreeNode<K,V> prev;    // 前驅(qū)節(jié)點(diǎn)(仍保留鏈表結(jié)構(gòu))
    boolean red;          // 顏色標(biāo)記
}

5.3. 實(shí)際測試數(shù)據(jù)

在Java標(biāo)準(zhǔn)庫的基準(zhǔn)測試中:

  • 節(jié)點(diǎn)數(shù)=6時(shí),鏈表比紅黑樹快約15%
  • 內(nèi)存占用減少約40%

6、常見誤區(qū)

1、誤區(qū):"紅黑樹會自動退化為鏈表"

事實(shí):這是HashMap的主動控制行為。

2、誤區(qū):"轉(zhuǎn)換會破壞數(shù)據(jù)"

事實(shí):元素順序和內(nèi)容完全保留。

3、誤區(qū):"節(jié)點(diǎn)數(shù)在7時(shí)會頻繁轉(zhuǎn)換"

事實(shí):只有在resize/remove時(shí)檢查閾值。

7、實(shí)戰(zhàn)建議

監(jiān)控樹節(jié)點(diǎn)比例

// 檢查桶的樹化情況
Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Node<?,?>[] table = (Node<?,?>[]) tableField.get(map);

int trees = 0;
for (Node<?,?> node : table) {
    if (node instanceof TreeNode) trees++;
}

優(yōu)化hashCode()

  • 減少哈希碰撞可避免樹化
  • 示例:
// 好的hashCode實(shí)現(xiàn)
@Override
public int hashCode() {
    return Objects.hash(field1, field2, field3); 
}

容量規(guī)劃

// 預(yù)設(shè)足夠大的initialCapacity
new HashMap<>(expectedSize * 2); 

總結(jié)

JDK的這個(gè)設(shè)計(jì)體現(xiàn)了工程上的精妙權(quán)衡:在保持算法理論正確性的同時(shí),針對實(shí)際硬件特性和使用場景做出了最優(yōu)實(shí)踐選擇。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java正則表達(dá)式API Matcher類方法

    Java正則表達(dá)式API Matcher類方法

    這篇文章主要介紹了Java正則表達(dá)式API Matcher類方法,對Matcher類的一些有用方法進(jìn)行功能對它們進(jìn)行分組展開介紹,需要的朋友可以參考一下
    2022-06-06
  • Java線程實(shí)現(xiàn)時(shí)間動態(tài)顯示

    Java線程實(shí)現(xiàn)時(shí)間動態(tài)顯示

    這篇文章主要為大家詳細(xì)介紹了Java線程實(shí)現(xiàn)時(shí)間動態(tài)顯示,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • idea ssm項(xiàng)目java程序使用十六進(jìn)制rxtx包向串口發(fā)送指令的方法

    idea ssm項(xiàng)目java程序使用十六進(jìn)制rxtx包向串口發(fā)送指令的方法

    這篇文章主要介紹了idea ssm項(xiàng)目java程序向串口發(fā)送指令并且使用十六進(jìn)制 rxtx包,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 一不小心就讓Java開發(fā)踩坑的fail-fast是個(gè)什么鬼?(推薦)

    一不小心就讓Java開發(fā)踩坑的fail-fast是個(gè)什么鬼?(推薦)

    這篇文章主要介紹了Java fail-fast,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Spring?Bean注冊與注入實(shí)現(xiàn)方法詳解

    Spring?Bean注冊與注入實(shí)現(xiàn)方法詳解

    首先,要學(xué)習(xí)Spring中的Bean的注入方式,就要先了解什么是依賴注入。依賴注入是指:讓調(diào)用類對某一接口的實(shí)現(xiàn)類的實(shí)現(xiàn)類的依賴關(guān)系由第三方注入,以此來消除調(diào)用類對某一接口實(shí)現(xiàn)類的依賴。Spring容器中支持的依賴注入方式主要有屬性注入、構(gòu)造函數(shù)注入、工廠方法注入
    2022-10-10
  • 深入分析Java異常

    深入分析Java異常

    本篇文章給大家詳細(xì)分享了關(guān)于Java異常的相關(guān)知識點(diǎn),對此有需要的朋友跟著學(xué)習(xí)下吧。
    2018-05-05
  • Springboot實(shí)現(xiàn)推薦系統(tǒng)的協(xié)同過濾算法

    Springboot實(shí)現(xiàn)推薦系統(tǒng)的協(xié)同過濾算法

    協(xié)同過濾算法是一種在推薦系統(tǒng)中廣泛使用的算法,用于預(yù)測用戶對物品(如商品、電影、音樂等)的偏好,從而實(shí)現(xiàn)個(gè)性化推薦,下面給大家介紹Springboot實(shí)現(xiàn)推薦系統(tǒng)的協(xié)同過濾算法,感興趣的朋友一起看看吧
    2025-05-05
  • Java Pattern與Matcher字符串匹配案例詳解

    Java Pattern與Matcher字符串匹配案例詳解

    這篇文章主要介紹了Java Pattern與Matcher字符串匹配案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • ThreadLocal工作原理及用法案例

    ThreadLocal工作原理及用法案例

    本文詳細(xì)講解了ThreadLocal工作原理及用法案例,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • Java項(xiàng)目命名規(guī)范參考

    Java項(xiàng)目命名規(guī)范參考

    在實(shí)際項(xiàng)目開發(fā)中,命名規(guī)范的遵守可以提高代碼的可讀性和可維護(hù)性,本文就來介紹一下Java項(xiàng)目命名規(guī)范參考,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11

最新評論