Dictionary 查找比 List 快因底层为哈希表,平均时间复杂度 O(1),而 List 需遍历、最坏 O(n);关键依赖 GetHashCode() 和 Equals() 的合理实现,推荐用 TryGetValue() 安全取值。
Dictionary 查找值为什么比 List 快
因为 dictionary 底层用哈希表实现,平均查找时间复杂度是 o(1);而 list<t></t> 是数组,按键查找得遍历,最坏 o(n)。但前提是键的 gethashcode() 和 equals() 实现合理——比如用 string 或 int 当键基本没问题,但自定义类当键时,不重写这两个方法就会查不到。
- 没重写
GetHashCode()的自定义键,每次调用返回不同哈希值,导致ContainsKey()返回false,哪怕键内容完全一样 -
null可以作为键存入(如果TKey是引用类型),但ContainsKey(null)会正常工作;而dict[null]会抛KeyNotFoundException - 值类型键(如
int)不会装箱,性能无额外开销;引用类型键要注意是否可能为null,尤其在 .NET 6+ 启用可空引用后编译器会警告
用 TryGetValue() 而不是先 ContainsKey() 再索引访问
两次查找等于白跑一趟哈希计算。.NET 官方文档明确建议:想安全取值,直接用 TryGetValue(),它一次哈希、一次判断、一次赋值,既快又线程友好(相比 ContainsKey() + [] 的非原子操作)。
- 错误写法:
if (dict.ContainsKey(key)) value = dict[key];—— 至少两次哈希查找 - 正确写法:
if (dict.TryGetValue(key, out var value)) { /* use value */ } - 如果确定键一定存在,且想省略检查,才用
dict[key];但一旦键不存在,立刻抛KeyNotFoundException,别指望它返回默认值
初始化 Dictionary 时指定容量能减少扩容开销
默认构造的 Dictionary 初始桶数组长度是 0,第一次 Add 就扩容到 3,之后按 2 倍增长(3→7→15→31…)。频繁 Add 会导致多次 rehash,尤其数据量大时明显拖慢。
- 已知要存 1000 个键值对,直接写
new Dictionary<string int>(1024)</string>,比默认构造快 10%~20% - 容量不是“最大允许数量”,而是内部哈希桶数组长度,设太大浪费内存,设太小触发多次扩容;经验上设为预估总数的 1.2~1.5 倍较稳妥
- 如果用
Dictionary<string, string>.Empty或ImmutableDictionary,那是另一回事——它们不可变,不适用此优化
KeyNotFoundException 不代表键真不存在,可能是 Equals 逻辑异常
当你确信键存在却仍抛 KeyNotFoundException,大概率是键类型的 Equals() 方法有 bug,或者哈希冲突处理出问题。比如自定义键里 GetHashCode() 基于一个字段算,Equals() 却比较另一个字段,结果哈希值相同但内容不等,查找就失败。
- 调试时打个断点,在
TryGetValue()前手动调用key.GetHashCode()和dict.Comparer.GetHashCode(key)看是否一致 - 用
dict.Keys.ToList()拿出所有键,逐个调key.Equals(你传的键),看哪个该返回 true 却返回 false - 避免在
GetHashCode()里用可变字段(如属性 setter 可改的字段),否则插入后字段一改,哈希值变,后续再也找不到这个键
事情说清了就结束










