ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【Unity小白学习日记8】C# 集合学习合集3——Dictionary

【Unity小白学习日记8】C# 集合学习合集3——Dictionary 目录✨一、Dictionary 常规使用1. 基础声明与初始化2. 高频API速查3. 取值的三种方式与坑点4. 遍历 Dictionary二、基本概念1. 什么是 Dictionary2. KeyValuePair 结构3. 键的哈希与相等性4. 常用初始化技巧三、散列表哈希表1. 散列表核心思想2. 哈希冲突3. 装填因子与扩容4. 时间复杂度分析5. 哈希函数的质量决定性能6. Dictionary vs Hashtable面试回答精简版总结一、Dictionary 常规使用DictionaryTKey, TValue是C#中最常用的键值对泛型集合命名空间为System.Collections.Generic。它以「键-值」形式存储数据键唯一不可重复通过键可以O(1)快速查找对应值是缓存、映射、配置管理场景的首选容器。1. 基础声明与初始化//声明空的Dictionary键为string值为int Dictionarystring, int scoreDict new Dictionarystring, int(); //初始化直接填充键值对 Dictionarystring, string configDict new Dictionarystring, string() { { PlayerName, 张三 }, { Level, 10 }, { Hp, 100 } };2. 高频API速查方法/属性功能说明Add(key, value)添加一个键值对键重复会抛异常Remove(key)根据键删除对应键值对返回是否删除成功Clear()清空所有键值对ContainsKey(key)判断是否存在指定键返回boolContainsValue(value)判断是否存在指定值返回boolTryGetValue(key, out value)安全取值键存在返回true并输出值不存在返回falseCount当前键值对总数Keys获取所有键的集合Values获取所有值的集合示例代码Dictionarystring, int score new Dictionarystring, int(); //添加 score.Add(数学, 95); score.Add(语文, 88); //安全取值推荐 if (score.TryGetValue(数学, out int mathScore)) { Console.WriteLine($数学成绩{mathScore}); } //判断键是否存在 if (score.ContainsKey(英语)) { score[英语] 90; //键存在则修改值 } else { score.Add(英语, 90); //不存在则添加 } //删除 score.Remove(语文); //遍历所有键 foreach (string key in score.Keys) { Console.WriteLine(${key}{score[key]}); } score.Clear();3. 取值的三种方式与坑点//方式1索引器直接取值 —— 键不存在会抛 KeyNotFoundException int s1 score[数学]; //方式2先判断再取值 —— 安全但查了两次字典 if (score.ContainsKey(数学)) { int s2 score[数学]; } //方式3TryGetValue —— 只查一次最推荐 ✅ if (score.TryGetValue(数学, out int s3)) { Console.WriteLine(s3); }重要坑点用索引器dict[key]取值时如果键不存在会直接抛异常。生产环境优先使用TryGetValue只做一次哈希查找性能更好且安全。4. 遍历 Dictionary//遍历键值对KeyValuePair foreach (KeyValuePairstring, int kvp in score) { Console.WriteLine(${kvp.Key}{kvp.Value}); } //只遍历键 foreach (string key in score.Keys) { Console.WriteLine(key); } //只遍历值 foreach (int value in score.Values) { Console.WriteLine(value); }注意和List一样foreach遍历过程中不能Add/Remove会抛出集合被修改异常。需要遍历删除时先把Keys转成List再遍历。//✅正确遍历删除 foreach (string key in score.Keys.ToList()) { if (score[key] 60) { score.Remove(key); } }二、基本概念1. 什么是 DictionaryDictionary 是基于散列表Hash Table实现的键值对集合。它不按插入顺序存储而是通过键的哈希值计算存储位置从而实现接近O(1)的查找、插入、删除效率。核心特征键唯一同一个键只能存在一个重复Add会抛异常键不可变作为键的对象其哈希值在存入后不能改变否则找不到数据无序遍历顺序不等于插入顺序.NET Core 3.0 实际保持插入顺序但这是实现细节不应依赖泛型强类型编译期确定键和值的类型无需装箱拆箱2. KeyValuePair 结构Dictionary 中每个元素都是一个KeyValuePairTKey, TValue结构体包含两个属性Key键Value值KeyValuePairstring, int kvp new KeyValuePairstring, int(年龄, 20); Console.WriteLine(${kvp.Key} {kvp.Value});3. 键的哈希与相等性Dictionary 判断两个键是否「相同」依赖两个方法GetHashCode()计算哈希值决定存储桶位置Equals()哈希冲突时判断两个键是否真的相等自定义类作为键时必须同时重写GetHashCode()和Equals()否则可能出现「逻辑上相等的两个对象被当成不同键」的问题。//自定义类作为键的正确写法 public class Player { public int Id { get; set; } public string Name { get; set; } public override bool Equals(object obj) { return obj is Player player Id player.Id; } public override int GetHashCode() { return HashCode.Combine(Id); //用Id计算哈希 } }4. 常用初始化技巧//集合初始化器C# 6 var dict1 new Dictionarystring, int { [A] 1, [B] 2 }; //从现有集合创建 var dict2 list.ToDictionary(item item.Id, item item.Name);三、散列表哈希表Dictionary 的底层数据结构就是散列表Hash Table理解散列表是掌握 Dictionary 性能的关键。1. 散列表核心思想散列表的核心是**「键 → 哈希值 → 数组下标 → 存储位置」**的映射对键调用GetHashCode()得到一个整数哈希值用哈希值对数组长度取模得到存储位置桶Bucket将键值对存入该位置对应的桶中理想情况下每个键对应唯一桶查找只需一次计算时间复杂度O(1)。2. 哈希冲突不同的键可能计算出相同的哈希值或取模后落到同一个桶这就是哈希冲突。Dictionary 解决冲突的方式拉链法Separate Chaining每个桶不只是存一个元素而是一个链表或数组冲突的元素都挂在同一个桶的链表里查找时先算哈希定位到桶再在桶内链表中用Equals()逐个比较找到目标桶数组 [0] → (键A,值1) → (键D,值4) ← 哈希冲突挂在同一桶 [1] → (键B,值2) [2] → (键C,值3) [3] → null3. 装填因子与扩容装填因子Load Factor 元素数量 / 桶数组长度装填因子越大冲突概率越高查找越慢.NET Dictionary 默认装填因子阈值约为1.0超过后触发扩容扩容机制新建一个长度约为原来2倍的桶数组重新计算所有元素的哈希值和新桶位置rehash将所有元素迁移到新数组旧数组交给GC回收✨性能优化预知数据量时初始化指定容量减少扩容和rehash开销Dictionarystring, int dict new Dictionarystring, int(1000);4. 时间复杂度分析操作平均情况最坏情况查找通过键O(1)O(n)所有键冲突到一个桶插入O(1)O(n)触发扩容时为O(n)删除O(1)O(n)遍历O(n)O(n)最坏情况O(n)极少出现只要哈希函数分布均匀实际性能始终接近O(1)。5. 哈希函数的质量决定性能一个好的哈希函数应该分布均匀不同键的哈希值尽量分散减少冲突计算快哈希计算本身不能太慢一致性相同的键必须返回相同的哈希值字符串的GetHashCode()在 .NET Core 中使用了改进的哈希算法分布均匀且每次进程启动不同防止哈希碰撞攻击。6. Dictionary vs Hashtable对比项DictionaryTKey,TValueHashtable类型安全泛型编译期检查非泛型存储object性能无装箱拆箱更快有装箱拆箱开销线程安全非线程安全部分线程安全已过时推荐度✅现代开发首选❌已被Dictionary替代面试回答精简版Dictionary底层是散列表通过键的哈希值取模定位存储桶哈希冲突用拉链法解决每个桶挂一个链表。查找、插入、删除平均O(1)最坏O(n)。装填因子超过阈值会触发2倍扩容并rehash。自定义类做键必须同时重写GetHashCode和Equals。生产环境取值优先用TryGetValue避免键不存在抛异常。总结常规使用掌握增删改查API牢记键唯一、取值优先用TryGetValue遍历删除需先转Keys为List。基本概念理解键值对结构、KeyValuePair、键的哈希与相等性自定义类做键必须重写两个方法。散列表原理哈希映射定位桶拉链法解决冲突装填因子控制扩容平均O(1)查找预估容量减少rehash是性能优化关键。✨小何同学路漫漫其修远兮吾将上下而求索✨
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进