应避免使用 Hashtable,优先选择 ConcurrentHashMap(线程安全)或 HashMap(单线程高效);它因全表同步、不支持 null、非 fail-fast、API 过时等问题已被淘汰。

别用 Hashtable,除非你在维护 JDK 1.0 时代的遗留系统。 它已被明确淘汰,ConcurrentHashMap 和 HashMap 覆盖了全部合理场景。
线程安全不是免费的:synchronized 方法拖垮性能
Hashtable 所有 public 方法(如 put()、get()、size())都加了 synchronized,相当于整张表一把大锁。哪怕两个线程操作完全不冲突的 key,也得排队——这是它在单线程/低并发下比 HashMap 慢 2–5 倍的主因。
- 多线程写入时,
Hashtable会严重阻塞;ConcurrentHashMap则分段加锁或使用 CAS,吞吐量高得多 -
HashMap完全不处理线程安全,直接裸奔——在单线程或已由外部同步(如synchronized块包裹)的场景下,它最轻快 - 想“临时线程安全”?
Collections.synchronizedMap(new HashMap())可行,但仅保证单个方法原子性,迭代 + 修改这类复合操作仍需手动同步
null 键值支持:一个报错,一个容忍
Hashtable 对 null 零容忍:put(null, "v") 或 put("k", null) 立刻抛 NullPointerException;而 HashMap 允许一个 null 键(存放在桶数组索引 0 处)和任意多个 null 值。
- 这是最常踩的坑:把
HashMap替换为Hashtable时,只要代码里存在map.put(key, value)且任一变量可能为null,运行期必崩 - 反向替换(
Hashtable→HashMap)通常安全,但要注意逻辑是否隐式依赖“null不可存”的行为(比如用get(k) == null判断 key 不存在——在HashMap中这可能是 key 不存在,也可能是 key 存在但值为null)
迭代器行为差异:fail-fast 是调试利器
HashMap 的 Iterator 是 fail-fast 的:遍历时若其他线程或同一线程未通过迭代器修改结构(如 remove()),会立即抛 ConcurrentModificationException;Hashtable 的 Enumeration(老式遍历)和 Iterator(因实现 Map 接口而支持)都不保证 fail-fast,可能返回脏数据或静默失败。
立即学习“Java免费学习笔记(深入)”;
- 这意味着:用
HashMap时,迭代中误调map.remove(k)会立刻暴露问题;用Hashtable可能跑完才出错,或结果不可靠 - 安全遍历删除必须用迭代器自身的
remove()方法,例如:Iterator
> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry e = it.next(); if (e.getValue() < 0) it.remove(); // ✅ 正确 }
继承与 API 设计:Dictionary 已成历史名词
Hashtable 继承自早已废弃的抽象类 Dictionary(JDK 1.0),而 HashMap 直接实现 Map 接口并继承 AbstractMap(JDK 1.2 引入)。这也导致部分 API 不一致:
-
Hashtable有contains(Object value)方法(易误解为查 key),HashMap已移除,只留containsKey()和containsValue() -
Hashtable初始容量是 11,扩容为2 * old + 1;HashMap初始为 16(2 的幂),扩容为old * 2,配合位运算优化 hash 定位 -
Hashtable没有computeIfAbsent()、merge()等 Java 8+ 函数式方法,强行用会触发编译错误
真正需要线程安全时,选 ConcurrentHashMap;单线程或可控并发,选 HashMap;Hashtable 唯一合理的存在理由,是对接某些强制要求其类型的老旧 API——这种情况下,务必确认 null 处理和迭代逻辑不会翻车。










