当前位置: 首页 > news >正文

Java HashMap 底层原理

一、JDK 1.7的HashMap源码解析

1. 数据结构
  • Entry类:链表节点定义,含key, value, hash, next指针
    源码节点:
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
        // 构造方法及get/set方法省略
    }
    
2. 插入过程(头插法)
  • put方法:计算哈希→处理冲突→扩容
    源码节点:
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) inflateTable(threshold); // 延迟初始化
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        // 遍历链表,若存在相同key则覆盖,否则头插新节点
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
        addEntry(hash, key, value, i); // 头插新节点
    }
    
3. 扩容死循环问题
  • transfer方法:头插法导致链表反转
    源码节点:
    void transfer(Entry[] newTable) {
        for (Entry<K,V> e : table) {
            while (e != null) {
                Entry<K,V> next = e.next;
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i]; // 头插法
                newTable[i] = e;
                e = next;
            }
        }
    }
    
    问题:多线程扩容时头插法导致链表成环。

二、JDK 1.8的HashMap改进

1. 数据结构升级
  • Node与TreeNode:链表节点与红黑树节点
    源码节点:
    // 普通链表节点
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
    }
    // 红黑树节点
    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent, left, right, prev;
        boolean red;
    }
    
2. 插入优化(尾插法)
  • putVal方法:尾插链表,树化逻辑
    源码节点:
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null); // 直接插入
        else {
            Node<K,V> e; K k;
            if (p.hash == hash && ((k = p.key) == key || key.equals(k)))
                e = p;
            else if (p instanceof TreeNode) // 红黑树插入
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null); // 尾插法
                        if (binCount >= TREEIFY_THRESHOLD - 1)
                            treeifyBin(tab, hash); // 链表转树
                        break;
                    }
                    // ...省略冲突处理
                }
            }
        }
    }
    
3. 哈希扰动优化
  • hash方法:简化扰动逻辑
    源码节点:
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    

三、ConcurrentHashMap的线程安全实现

1. JDK 1.7的分段锁
  • Segment类:继承ReentrantLock,锁分段
    源码节点:
    static final class Segment<K,V> extends ReentrantLock {
        transient volatile HashEntry<K,V>[] table;
        // put方法加锁
        final V put(K key, int hash, V value, boolean onlyIfAbsent) {
            HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(...);
            try {
                // ...插入逻辑
            } finally {
                unlock();
            }
        }
    }
    
2. JDK 1.8的CAS+synchronized
  • Node与CAS操作:无锁化初始化与插入
    源码节点:
    // 初始化表(CAS控制并发)
    private final Node<K,V>[] initTable() {
        while ((tab = table) == null || tab.length == 0) {
            if ((sc = sizeCtl) < 0) Thread.yield();
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    // 创建新数组
                } finally {
                    sizeCtl = sc;
                }
            }
        }
    }
    
    // putVal中的synchronized块
    synchronized (f) {
        if (tabAt(tab, i) == f) {
            if (fh >= 0) {
                // 链表插入
            } else if (f instanceof TreeBin) {
                // 红黑树插入
            }
        }
    }
    
3. 扩容协作
  • helpTransfer方法:多线程协助数据迁移
    源码节点:
    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        if (tab != null && f instanceof ForwardingNode) {
            while (nextTab == nextTable && table == tab) {
                // 协助扩容线程完成数据迁移
            }
        }
    }
    

四、核心差异总结

特性JDK 1.7JDK 1.8
HashMap数据结构数组+单向链表(Entry类)数组+链表/红黑树(Node/TreeNode类)
ConcurrentHashMap锁分段锁(Segment类)细粒度锁(synchronized+CAS)
插入方式头插法(transfer方法成环)尾插法(putVal方法避免死循环)
哈希计算4次扰动(复杂位运算)1次扰动(h ^ (h >>> 16))

源码验证总结

  • HashMap 1.7:通过Entry类、transfer方法验证链表结构与死循环问题。
  • HashMap 1.8:通过TreeNode类、putVal方法验证树化逻辑。
  • ConcurrentHashMap 1.7:通过Segment类与锁机制。
  • ConcurrentHashMap 1.8:通过Node类、initTable的CAS与synchronized块。

相关文章:

  • 【YOLO】AutoDL 训练模型
  • Python+DeepSeek:开启AI编程新次元——从自动化到智能创造的实战指南
  • Java三种注释方式
  • 【漫话机器学习系列】132.概率质量函数(Probability Mass Function, PMF)
  • 体验开源openeuler openharmony stratovirt模拟器
  • Linux内核实时机制18 - RT调度器1 - 数据结构
  • hive开窗函数
  • JavaScript性能优化实战
  • 第四十五篇-Tesla P40关闭GPU的ECC释放部分显存
  • 刷leetcode hot100--动态规划3.11
  • 指针的比较
  • MQTT 物联网的首先协议
  • 小程序 wxml 语法 —— 36 wxml 语法 - setData() 修改数据
  • 基于协同过滤算法的音乐推荐系统(源码+部署教程)
  • 【华三(H3C)交换机上修改 NTP 配置】
  • Docker安装Kafka(内含zookeeper)
  • 第二章:盒模型的奥秘
  • 每天一道算法题【蓝桥杯】【使用最小花费爬楼梯】
  • 扩散 Transformer 策略:用于通才视觉-语言-动作学习的规模化扩散 Transformer
  • 51c大模型~合集10
  • 人民日报钟声:中方维护自身发展利益的决心不会改变
  • 巴国家安全委员会授权军方自主决定对印反击措施
  • 无人机穿越大理崇圣寺千年古塔时“炸机”,当地:肇事者已找到,将被追责
  • 外交部:中欧关系50年发展最宝贵经验是相互尊重,求同存异
  • 机关食堂向游客开放的重庆荣昌区,“消费市场迎来历史性突破”
  • 江西省文化和旅游厅厅长梅亦已任省委宣传部副部长