




HashMap允许null键和null值,Hashtable不允许任何null;HashMap默认容量16(2的幂,位运算寻址),Hashtable默认11(奇数,取模寻址);HashMap有红黑树优化,Hashtable无;Hashtable迭代器不fail-fast,HashMap迭代器会抛ConcurrentModificationException。
null?一写就报错的坑这是开发中最常踩的雷:Hashtable 对 null 零容忍,只要 put(null, "x") 或 put("k", null),立刻抛 NullPointerException;而 HashMap 允许一个 null 键(存在数组索引 0 的位置)和任意多个 null 值。
null 表达为 key,只
HashMap
Hashtable 时别依赖 IDE 自动补全——它不会提前告诉你 put 方法内部有 if (key == null) throw new NullPointerException()
Collections.synchronizedMap(new HashMap()),null 支持仍保留,但线程安全是“假安全”(全局锁,性能差)Hashtable 真的安全吗?语法上安全,逻辑上危险。它的所有方法都加了 synchronized,但锁的是整个对象——相当于排队打饭,一个人打完,下一个人才能进窗口。并发高时,get 和 put 互相阻塞,吞吐量断崖下跌。
ConcurrentHashMap 是现代替代方案:JDK 1.8+ 用 CAS + synchronized 分段锁(实际是 Node 数组分桶加锁),读操作无锁,写操作只锁冲突桶Hashtable 当共享缓存——哪怕 QPS 只有 50,响应延迟也可能翻倍put / remove,否则扩容时的锁竞争会更严重HashMap 默认容量是 16,而 Hashtable 是 11?这不是随意定的数字,背后是哈希寻址效率差异:
HashMap 容量必须是 2 的幂(16、32、64…),这样算桶索引用位运算 hash & (capacity - 1),比取模快一个数量级Hashtable 容量默认 11(奇数),扩容公式是 newCapacity = oldCapacity * 2 + 1,设计初衷是让 hash % capacity 分布更均匀——但现代 CPU 上,取模成本远高于位运算,这个优化已失效HashMap 平均耗时约 8ms,Hashtable 约 14ms(JDK 17,无并发)HashMap 的红黑树优化,Hashtable 为什么没有?因为 Hashtable 是 JDK 1.0 的遗留类,从诞生就没考虑过海量哈希冲突场景;而 HashMap 在 JDK 1.8 引入红黑树,专门解决“链表过长 → 查询退化成 O(n)”的问题。
链表长度 ≥ 8 且 数组容量 ≥ 64,才转红黑树;反之,节点 ≤ 6 就降级回链表hashCode() 实现不合理(比如总返回同一个值),Hashtable 的查询会随数据增长线性变慢,而 HashMap 仍能保持 O(log n)initialCapacity 规避——真正要治本,得重写 key 类的 hashCode() 和 equals()
真正容易被忽略的点是:Hashtable 的 Enumeration 迭代器不支持 fail-fast,遍历时被其他线程修改也不会报错,结果可能漏数据或重复读;而 HashMap 的 Iterator 一旦检测到并发修改,立刻抛 ConcurrentModificationException——这看似是“缺点”,其实是帮你提前暴露线程安全问题。