HashMap、Hashtable、ConcurrentHashMap 区别详解 在 Java 集合框架中HashMap、Hashtable、ConcurrentHashMap 都是基于哈希表实现的键值对容器三者最核心的差异集中在线程安全机制、锁粒度设计、并发性能三个维度也是 Java 并发面试的高频基础考点。本文从底层实现到特性差异逐一拆解帮你彻底理清三者的区别与选型逻辑。一、各实现详细解析1. HashMap单线程性能首选HashMap 是日常开发最常用的哈希表实现不做任何线程安全处理因此单线程环境下没有锁开销性能最高。核心特性底层结构JDK1.8 之后采用「数组 链表 / 红黑树」实现。当链表长度超过 8 且数组长度大于 64 时链表会转换为红黑树解决哈希冲突严重时的查询性能退化问题。空值支持允许最多 1 个 key 为 null允许多个 value 为 null。并发风险多线程环境下会出现数据覆盖、size 计数不准、扩容死循环JDK1.7 头插法等异常禁止在并发场景下直接使用。2. HashtableHashtable 是 JDK 早期提供的线程安全哈希表实现方式非常粗暴所有核心方法都加上 synchronized 关键字直接锁整个 Hashtable 对象。核心特性锁粒度极粗整张表只有一把锁。无论线程操作哪个位置的数据都会产生锁竞争并发性能极差。扩容开销大触发扩容时由单个线程完成全部元素的拷贝迁移过程耗时久。不支持 nullkey 和 value 都不允许为 null否则会抛出空指针异常。现状锁设计过于原始性能低下目前已经被 ConcurrentHashMap 完全替代仅作为历史遗留类存在不推荐在新代码中使用。3. ConcurrentHashMap高并发场景的最优解ConcurrentHashMap 是为并发场景设计的线程安全哈希表。以Java1.8为例彻底取消了分段锁改用更细粒度的桶级锁并做了多处优化读操作无锁读取数据时不加锁通过 volatile 关键字保证数据的内存可见性直接从主内存读取最新值。写操作锁桶仅对当前操作的哈希桶链表头结点加 synchronized 锁只有两个线程恰好操作同一个桶时才会冲突锁粒度远小于分段锁。CAS 辅助更新size 计数等属性通过 CAS 原子更新避免重量级锁。化整为零扩容扩容时新老数组同时存在每个操作哈希表的线程都会协助迁移一部分元素分散扩容开销避免单线程长时间阻塞。扩容期间插入只写入新数组查询需要同时检索新老数组全部元素迁移完成后再删除老数组。不支持 null和 Hashtable 一样禁止 null 键值。原因是并发场景下get 返回 null 无法区分「值本身是 null」还是「key 不存在」会产生语义歧义。二、核心演进逻辑锁粒度不断细化三者的性能差异本质上是锁粒度不断细化的演进过程Hashtable全表一把锁 → 所有操作都冲突性能最差ConcurrentHashMap 1.8桶级锁 → 同桶才冲突性能最优锁的粒度越细并发冲突的概率就越低高并发下的性能表现就越好。三、选型总结单线程环境优先使用 HashMap无锁开销性能最高。多线程环境使用 ConcurrentHashMap兼顾线程安全与并发性能是目前的工业标准方案。Hashtable 仅作为历史知识了解不要在新项目中使用。