HashMap 原理

返回 面试 · 速查全集 Java常见面试题

JDK 8+:数组 + 链表 + 红黑树;非线程安全。详解 集合框架


结构

table[]  ──►  [k1,v1] → [k9,v9] → null        (链表)
           ──►  红黑树(桶内节点 ≥ 8 且 table.length ≥ 64)
           ──►  null
参数默认值说明
初始容量16必为 2 的幂
负载因子0.75元素数 > capacity×0.75 时扩容
扩容2 倍rehash 到新数组

put 流程(简述)

  1. 计算 hash = (h = key.hashCode()) ^ (h >>> 16)(扰动,减少碰撞)
  2. index = (n - 1) & hash 定位桶
  3. 桶空 → 直接放入
  4. 桶非空 → 比 key(先 hash 再 equals
    • 相同 key → 覆盖 value
    • 不同 key → 挂链表/插入红黑树
  5. 超阈值 → resize 扩容

链表转红黑树

条件说明
链表长度 ≥ 8且 table.length ≥ 64 时树化
树节点 ≤ 6退化回链表

目的:最坏 O(n) 查改降为 O(log n)。


为什么容量是 2 的幂

(n - 1) & hash 等价于对 n 取模,且位运算更快;保证索引均匀分布。


线程不安全

并发 put 可能导致:数据丢失死循环(JDK7 扩容头插法,已修复)、size 不准

场景替代
高并发读写ConcurrentHashMap
需排序TreeMap
需插入顺序LinkedHashMap

ConcurrentHashMap 简对比

版本实现
JDK 7Segment 分段锁
JDK 8+CAS + synchronized 锁桶头节点,粒度更细

ConcurrentHashMap


常见面试题

Q:HashMap 允许 null 吗?
A:允许 一个 null key、多个 null value;ConcurrentHashMap 不允许 null key/value。

Q:HashSet 如何去重?
A:底层 HashMap,元素作 key,value 为固定 PRESENT;依赖 hashCode + equals

Q:重写 equals 为什么要重写 hashCode
A:相等对象 hash 必须相同,否则 HashMap/HashSet 找不到。见 equals与hashCode

Q:JDK7 和 JDK8 HashMap 区别?
A:8 引入红黑树;扩容 rehash 算法优化;头插改尾插,避免并发扩容环链。


相关笔记