Java HashMap 和 Hashtable

AI的出现,是否能替代IT从业者? 10w+人浏览 1.6k人参与

在 Java 中,HashMapHashtable 都是 Map 接口的实现类,用于存储键值对(key-value)数据(key 唯一、value 可重复),但二者在 线程安全、null 值支持、底层实现、性能 等核心维度差异显著。以下是详细解析:

一、核心概述

  • 两者本质都是 “哈希表”,通过哈希函数将 key 映射到数组索引,解决哈希冲突(链表 / 红黑树)。

  • Hashtable 是 JDK 1.0 推出的 遗留类,设计偏老旧;HashMap 是 JDK 1.2 引入的 Collections Framework 核心类,专为高效性设计,是目前开发中最常用的 Map 实现。

二、核心区别(重点)

1. 线程安全与并发性能

特性HashtableHashMap
线程安全是(线程安全)否(非线程安全)
实现方式所有方法加 synchronized 锁(全局锁)无锁设计(默认非线程安全)
并发性能极低(多线程竞争同一把锁,阻塞严重)高(无锁 overhead,单线程效率极高)
  • 补充:若需线程安全的 HashMap,可使用 Collections.synchronizedMap(new HashMap<>())(本质也是全局锁,效率和 Hashtable 接近),更推荐用 ConcurrentHashMap(JDK 1.8+ 用 CAS + 分段锁,并发性能远超 Hashtable)。

2. null 值支持

这是开发中最易踩坑的区别:

这是开发中最易踩坑的区别:

  • HashMap:允许 1 个 null key(key 唯一)和多个 null value。

    HashMap<String, String> map = new HashMap<>();
    map.put(null, "a"); // 合法
    map.put(null, "b"); // 覆盖前一个 null key 的值(最终 null → "b")
    map.put("c", null); // 合法
    map.put("
            d", null); // 合法(多个 null value)

    Hashtable:禁止 null key 和 null value,否则直接抛出 NullPointerException

    Hashtable<String, String> table = new Hashtable<>();
    table.put(null, "a"); // 抛 NPE
    table.put("b", null); // 抛 NPE

3. 底层数据结构

  • HashMap (JDK 1.8+): 数组 + 链表 + 红黑树 (优化哈希冲突)

    • 数组(哈希桶)是核心,每个元素是链表的头节点;

    • 当链表长度 > 8 且数组容量 ≥ 64 时,链表转为红黑树(查询时间复杂度从 O (n) 降为 O (log n));

    • 当红黑树节点数 < 6 时,转回链表(节省空间)。

  • Hashtable: 始终是数组 + 链表 (无红黑树优化)

    • 哈希冲突时仅用链表解决,当数据量大时,链表过长会导致查询效率急剧下降(O (n))。

4. 初始容量与扩容机制

哈希表的 “容量” 是数组的长度,“负载因子”(默认 0.75)是扩容阈值的比例(容量 × 负载因子),当元素个数超过阈值时触发扩容。

特性HashtableHashMap
初始容量1116(必须是 2 的幂)
扩容规则新容量 = 旧容量 × 2 + 1新容量 = 旧容量 × 2(保持 2 的幂)
负载因子默认 0.75(可通过构造器修改)默认 0.75(可通过构造器修改)
  • 关键:HashMap 容量为 2 的幂,是为了用 位运算(&)替代取模(%) 计算哈希索引(index = hash & (capacity - 1)),效率更高(位运算比取模快);而 Hashtable 用取模计算索引(index = (hash & 0x7FFFFFFF) % capacity),效率较低。

5. 哈希计算方式

  • Hashtable :直接使用 key 的hashCode() 作为哈希值,无二次处理:

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % capacity; // 取模确保索引非负
  • HashMap :对 key 的hashCode() 进行 二次哈希 (hash() 方法),减少哈希冲突:

static final int hash(Object key) {
    int h;
    // key 为 null 时 hash 为 0,否则二次哈希(高位参与运算)
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
int index = hash & (capacity - 1); // 位运算求索引

二次哈希的目的是让 key 的高位哈希值也参与索引计算,降低不同 key 映射到同一索引的概率。

6. 迭代器特性(fail-fast vs 非 fail-fast)

  • HashMap:迭代器(Iterator )是 fail-fast(快速失败) 的:

    • 迭代过程中,若通过非迭代器方法(如 putremove)修改集合,会立即抛出 ConcurrentModificationException(并发修改异常);

    • 原理:迭代器维护一个 modCount(修改次数),每次迭代时检查 modCount 是否变化。

  • Hashtable :枚举器(Enumerator )是 非 fail-fast 的:

    • 迭代过程中修改集合(如 putremove)不会抛出异常,但迭代器本身不支持 remove() 方法(调用会抛 UnsupportedOperationException);

    • 注意:Hashtable 也提供 Iterator 迭代器(兼容 Collections Framework),其 Iterator 同样是 fail-fast 的,仅原生 Enumerator 是非 fail-fast。

7. 继承关系与设计定位

  • Hashtable:继承自 Dictionary 类(已废弃的抽象类),同时实现 Map 接口,设计上存在历史包袱。

  • HashMap:直接实现 Map 接口,遵循 Collections Framework 规范,设计更简洁,功能更灵活(如支持 forEachcompute 等 JDK 8+ 新方法)。

三、完整对比表格

对比维度HashtableHashMap
线程安全是(synchronized 全局锁)否(默认非线程安全)
null 支持禁止 key/value 为 null(抛 NPE)允许 1 个 null key + 多个 null value
底层结构数组 + 链表(无红黑树)数组 + 链表 + 红黑树(JDK 1.8+)
初始容量1116(2 的幂)
扩容规则2n + 12n(保持 2 的幂)
哈希计算直接用 hashCode () + 取模二次哈希(hash ()) + 位运算
迭代器Enumerator(非 fail-fast)、Iterator(fail-fast)Iterator(fail-fast)
效率低(全局锁 + 链表查询慢)高(无锁 + 红黑树优化)
设计定位JDK 1.0 遗留类JDK 1.2+ 主流 Map 实现
推荐场景几乎不用(被 ConcurrentHashMap 替代)单线程 / 无并发场景(首选)

四、使用场景与推荐实践

  1. 单线程环境(无并发修改)

    • 优先用 HashMap(效率最高,支持 null 值,功能灵活);

    • 若需 “插入顺序 / 访问顺序”,用 LinkedHashMapHashMap 子类);

    • 若需排序(如按 key 自然排序),用 TreeMap

  2. 多线程并发环境

    • 首选 ConcurrentHashMap(JDK 1.8+ 并发性能最优,支持高并发读写,无 null 键值限制);

    • 避免用 HashtableCollections.synchronizedMap(new HashMap<>())(二者都是全局锁,并发效率低)。

  3. 禁止 null 键值的场景

    • 不用 Hashtable,可通过业务逻辑校验 null,或用 ConcurrentHashMap(手动避免 null 键值)。

  4. 历史项目兼容

    • 仅当维护老旧项目(JDK 1.0 遗留代码)时,才可能接触 Hashtable,新项目坚决不用。

五、关键总结

  • HashMap 是单线程场景的首选,高效、灵活、功能完善;

  • Hashtable 是遗留类,线程安全但效率低、限制多(无 null 支持、无红黑树),已被 ConcurrentHashMap 替代;

  • 核心选择逻辑:单线程用 HashMap,多线程用 ConcurrentHashMap

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值