应优先使用 Dictionary,仅在维护老旧 .NET Framework 1.x/2.0 代码或需运行时混存多种键类型时才用 Hashtable;后者存在装箱开销、O(n) 查找、伪线程安全及遍历无序等缺陷。

什么时候该用 Hashtable,而不是 Dictionary
绝大多数新项目里,Hashtable 已不是首选。它只在两类场景下仍有实际价值:
一是维护非常老的 .NET Framework 1.x/2.0 代码,且升级成本过高;二是需要运行时动态混存多种键类型(比如同一集合里同时存 int 键、string 键、Guid 键),且明确接受类型不安全和性能损耗。
-
Hashtable允许hashtable.Add(1, "a")和hashtable.Add("key", DateTime.Now)混用,Dictionary编译直接报错 - 但代价是:每次取值都要强制转型,比如
(string)hashtable[1],一旦类型写错,程序在运行时才崩 - .NET Core / .NET 5+ 中
Hashtable已被标记为“遗留类型”,官方文档明确建议迁移到Dictionary或线程安全的ConcurrentDictionary
Hashtable 的基本用法和典型陷阱
初始化后,所有键值都以 object 存储,遍历时必须用 DictionaryEntry,不能直接解构:
Hashtable ht = new Hashtable();
ht.Add("name", "Alice");
ht.Add(42, true);
// ✅ 正确遍历方式
foreach (DictionaryEntry entry in ht)
{
Console.WriteLine($"{entry.Key} → {entry.Value}");
}
// ❌ 错误:不能直接 foreach (var kv in ht)
// ❌ 错误:ht["missing"] 返回 null,不是抛异常 —— 容易掩盖逻辑错误
- 访问不存在的键(如
ht["xxx"])返回null,不会抛KeyNotFoundException,容易漏掉空引用异常 -
ContainsKey和ContainsValue都是 O(n) 查找(因为 value 未索引),大数据量时很慢 - 键为
null是允许的(仅一个),但值为null完全没问题 —— 这和Dictionary对 null 键的严格限制完全不同
性能差异到底差在哪?不只是“泛型 vs 非泛型”
关键瓶颈在值类型操作:int、bool、DateTime 等作为键或值时,Hashtable 必然触发装箱(boxing),而 Dictionary 完全避免。
- 实测:10 万次插入
int→string,Dictionary比Hashtable快 2–3 倍(.NET 6 环境) -
Hashtable内部每个节点是DictionaryEntry对象,堆分配频繁,GC 压力大;Dictionary用紧凑数组 + 索引管理,缓存友好 - 哈希冲突处理上,
Dictionary在 .NET Core 2.1+ 中链表长度 >8 自动转红黑树,最坏查找从 O(n) 降到 O(log n);Hashtable始终是链表,无此优化
线程安全别被“文档误导”
文档说 Hashtable.Synchronized() 是线程安全的,但实际是“伪安全”——它只保证单个方法原子性(如单次 Add 不会中断),不保证多步操作的事务性。例如“检查是否存在再添加”这种常见模式依然会出错:
var syncHt = Hashtable.Synchronized(new Hashtable());
if (!syncHt.ContainsKey("x")) {
syncHt.Add("x", "val"); // ❌ 仍可能并发重复添加!
-
Dictionary默认完全不加锁,多线程写必须自己用lock包裹整段逻辑,性能损失明显 - 真要线程安全,应直接用
ConcurrentDictionary,它针对读多写少做了精细分段锁,比包装后的Hashtable实际吞吐更高 - 注意:
Hashtable的“线程安全”仅限于 .NET Framework;.NET Core 中Synchronized()方法已移除,调用即抛PlatformNotSupportedException
真正容易被忽略的是:Hashtable 的无序性不是“偶尔乱”,而是完全不可预测——哪怕插入顺序固定,不同 .NET 版本、不同 CPU 架构下遍历结果都可能不同;而 Dictionary 自 .NET Framework 3.5 起就稳定保持插入顺序,这对调试和日志可重现性至关重要。







