java打卡学习4:HashMap底层结构、扩容机制

HashMap底层结构

HashMap的底层结构在JDK1.8之前是数组+链表 ,JDK1.8及之后改为数组+链表+红黑树

  • 数组 :称为哈希桶(Node<K,V>[] table),用于存储键值对的节点。数组的每个位置称为一个桶(bucket),通过哈希函数计算键的哈希值确定存储位置。
  • 链表:当哈希冲突(不同键的哈希值映射到同一数组下标)时,采用链表解决冲突,新节点以头插法(JDK1.7)或尾插法(JDK1.8)加入链表。
  • 红黑树:当链表长度超过阈值(默认为8)且数组长度≥64时,链表转换为红黑树,以提高查询效率(时间复杂度从O(n)降至O(log n))。

HashMap扩容机制

HashMap的扩容通过resize()方法实现,核心逻辑如下:

  • 触发条件

    1. 当前元素数量超过阈值(threshold = capacity * loadFactor,默认负载因子loadFactor=0.75)。
    2. 链表长度≥8但数组长度<64时,优先扩容而非树化。
  • 扩容过程

    1. 创建新数组,容量为原数组的2倍(newCap = oldCap << 1)。
    2. 重新计算节点位置:
      • 若节点无冲突(无链表/红黑树),直接按(newCap - 1) & hash确定新位置。
      • 若节点是树节点,拆分红黑树为两条链表(根据高位哈希值),若链表长度≤6则退化为链表。
      • 若节点是链表节点,拆分为两条链表(高位链表和低位链表),分别放入新数组的原位置原位置 + oldCap
  • 性能优化

    JDK1.8通过高位与低位链表拆分,避免重新计算哈希值,提升扩容效率。

关键参数

  • 初始容量:默认16,建议设为2的幂次方(便于哈希计算)。
  • 负载因子:默认0.75,权衡空间利用率与哈希冲突概率。

示例代码(哈希计算与扩容片段)

java 复制代码
// JDK1.8中的哈希计算(扰动函数)
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

// 扩容时的节点位置计算
if ((e.hash & oldCap) == 0) {
    // 放入低位链表(原位置)
} else {
    // 放入高位链表(原位置 + oldCap)
}

代码模拟

以下是一个简化版的HashMap扩容模拟代码:

java 复制代码
public class SimulatedHashMap<K, V> {
    private static final int DEFAULT_CAPACITY = 16;
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    private Entry<K, V>[] table;
    private int size;
    private int threshold;

    public SimulatedHashMap() {
        table = new Entry[DEFAULT_CAPACITY];
        threshold = (int) (DEFAULT_CAPACITY * DEFAULT_LOAD_FACTOR);
    }

    public void put(K key, V value) {
        if (size >= threshold) {
            resize();
        }
        int hash = key.hashCode();
        int index = hash % table.length;
        Entry<K, V> entry = new Entry<>(key, value, hash);
        if (table[index] == null) {
            table[index] = entry;
        } else {
            Entry<K, V> current = table[index];
            while (current.next != null) {
                current = current.next;
            }
            current.next = entry;
        }
        size++;
    }

    private void resize() {
        int newCapacity = table.length * 2;
        Entry<K, V>[] newTable = new Entry[newCapacity];
        for (Entry<K, V> entry : table) {
            while (entry != null) {
                int newIndex = entry.hash % newCapacity;
                Entry<K, V> next = entry.next;
                entry.next = newTable[newIndex];
                newTable[newIndex] = entry;
                entry = next;
            }
        }
        table = newTable;
        threshold = (int) (newCapacity * DEFAULT_LOAD_FACTOR);
    }

    static class Entry<K, V> {
        K key;
        V value;
        int hash;
        Entry<K, V> next;

        Entry(K key, V value, int hash) {
            this.key = key;
            this.value = value;
            this.hash = hash;
        }
    }
}

关键点说明

  • 负载因子:默认0.75,权衡空间和时间效率。较高的负载因子减少内存占用但增加冲突概率。
  • rehash:扩容时重新计算每个元素的哈希值和新位置,可能改变原有链表的顺序。
  • 链表处理:Java 8后在链表长度超过8时会转换为红黑树,优化查找性能。

性能影响

扩容是一个昂贵的操作,时间复杂度为O(n)。为避免频繁扩容,初始化时可以预估容量大小,例如:

java 复制代码
Map<String, String> map = new HashMap<>(expectedSize);

HashMap底层结构是数组+链表/红黑树(JDK8及以后),扩容机制是当元素数量超过(容量×负载因子)时触发扩容,扩容时创建双倍容量新数组,并通过高位运算重新计算节点位置。

相关推荐
我命由我1234510 小时前
Android Studio - Android Studio 自定义预览尺寸
android·java·ide·java-ee·android studio·android-studio·android runtime
咖啡八杯11 小时前
GoF设计模式——迭代器模式
java·后端·设计模式·迭代器模式
AIGS00111 小时前
企业AI落地的关键认知:向量空间JBoltAI的本体语义平台
java·人工智能·人工智能ai大模型应用
KaMeidebaby12 小时前
卡梅德生物技术快报|抗体亲和力成熟工业化调控新机制:差异性浆细胞增殖工艺优化思路
java·开发语言·人工智能·算法·机器学习·架构·spark
心中有国也有家12 小时前
AtomGit Flutter 鸿蒙客户端: ChangeNotifier 模式
学习·flutter·华为·harmonyos
取地址符13 小时前
C++学习笔记(基于learn-cxx)(1)
c++·经验分享·笔记·学习
醉城夜风~13 小时前
Java详解经典算法题:接雨水(三种实现方案+原理剖析)
java·开发语言·算法
恣逍信点14 小时前
主观是客观的聚焦
人工智能·学习·程序人生·生活·业界资讯·交友·哲学
打球她不香吗15 小时前
本周ALLegro学习内容的总结
学习·allegro
数智化转型推荐官15 小时前
2026统一身份管理系统五大发展趋势解读
java·运维·微服务