410 lines
8.3 KiB
Markdown
410 lines
8.3 KiB
Markdown
|
|
# Dictionary
|
|||
|
|
概念:
|
|||
|
|
`Dictionary<TKey, TValue>` 是 C# 提供的==泛型键值对集合==
|
|||
|
|
它通过键 `key` 来快速查找值 `value`
|
|||
|
|
|
|||
|
|
简单理解:
|
|||
|
|
像一本字典
|
|||
|
|
用“词”查“解释”
|
|||
|
|
用 `key` 查 `value`
|
|||
|
|
|
|||
|
|
命名空间:
|
|||
|
|
```csharp
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 通常用法
|
|||
|
|
1. 保存键值对数据
|
|||
|
|
2. 通过唯一键快速查找对应值
|
|||
|
|
3. 根据 `id`、名字、编号等信息管理对象
|
|||
|
|
4. 替代早期非泛型键值对集合 `Hashtable`
|
|||
|
|
|
|||
|
|
实际开发中:
|
|||
|
|
`Dictionary<TKey, TValue>` 非常常用
|
|||
|
|
只要需求是“通过 key 快速找到 value”
|
|||
|
|
通常都可以先考虑它
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 语法
|
|||
|
|
创建:
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
添加:
|
|||
|
|
```csharp
|
|||
|
|
dict.Add("语文", 90);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
通过键取值:
|
|||
|
|
```csharp
|
|||
|
|
int score = dict["语文"];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
修改:
|
|||
|
|
```csharp
|
|||
|
|
dict["语文"] = 95;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
数量:
|
|||
|
|
```csharp
|
|||
|
|
dict.Count
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 基础案例
|
|||
|
|
```csharp
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
|
|||
|
|
Dictionary<string, int> scores = new Dictionary<string, int>();
|
|||
|
|
|
|||
|
|
scores.Add("语文", 90);
|
|||
|
|
scores.Add("数学", 95);
|
|||
|
|
scores.Add("英语", 88);
|
|||
|
|
|
|||
|
|
Console.WriteLine(scores["语文"]);
|
|||
|
|
Console.WriteLine(scores["数学"]);
|
|||
|
|
Console.WriteLine(scores["英语"]);
|
|||
|
|
Console.WriteLine(scores.Count);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
输出:
|
|||
|
|
```text
|
|||
|
|
90
|
|||
|
|
95
|
|||
|
|
88
|
|||
|
|
3
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
理解:
|
|||
|
|
1. `"语文"`、`"数学"`、`"英语"` 是键
|
|||
|
|
2. `90`、`95`、`88` 是值
|
|||
|
|
3. 通过 key 能快速找到对应 value
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# Add 和索引器
|
|||
|
|
使用 `Add` 添加:
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
|||
|
|
|
|||
|
|
dict.Add("hp", 100);
|
|||
|
|
dict.Add("mp", 50);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
使用索引器新增或修改:
|
|||
|
|
```csharp
|
|||
|
|
dict["atk"] = 20; // key 不存在时,新增
|
|||
|
|
dict["atk"] = 25; // key 已存在时,修改
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
区别:
|
|||
|
|
1. `Add` 遇到重复 key 会报错
|
|||
|
|
2. `dict[key] = value` 遇到重复 key 会覆盖旧值
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 增删查改
|
|||
|
|
增加:
|
|||
|
|
```csharp
|
|||
|
|
dict.Add("def", 10);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
删除:
|
|||
|
|
```csharp
|
|||
|
|
dict.Remove("def");
|
|||
|
|
dict.Clear();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
查找键:
|
|||
|
|
```csharp
|
|||
|
|
bool hasHp = dict.ContainsKey("hp");
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
查找值:
|
|||
|
|
```csharp
|
|||
|
|
bool hasValue = dict.ContainsValue(100);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
修改:
|
|||
|
|
```csharp
|
|||
|
|
dict["hp"] = 120;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
取值:
|
|||
|
|
```csharp
|
|||
|
|
int hp = dict["hp"];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# TryGetValue 很常用
|
|||
|
|
直接取值:
|
|||
|
|
```csharp
|
|||
|
|
int hp = dict["hp"];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
问题:
|
|||
|
|
如果 key 不存在
|
|||
|
|
会报错
|
|||
|
|
|
|||
|
|
更稳的写法:
|
|||
|
|
```csharp
|
|||
|
|
if (dict.TryGetValue("hp", out int hp))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine(hp);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("没有这个键");
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
说明:
|
|||
|
|
1. `TryGetValue` 会返回 `true` 或 `false`
|
|||
|
|
2. 如果找到 key,就把对应值放进 `out` 变量
|
|||
|
|
3. 如果项目里经常“先查再取”,`TryGetValue` 很实用
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 遍历
|
|||
|
|
遍历键值对:
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
|||
|
|
|
|||
|
|
dict.Add("语文", 90);
|
|||
|
|
dict.Add("数学", 95);
|
|||
|
|
|
|||
|
|
foreach (KeyValuePair<string, int> item in dict)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine(item.Key + " : " + item.Value);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
只遍历键:
|
|||
|
|
```csharp
|
|||
|
|
foreach (string key in dict.Keys)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine(key);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
只遍历值:
|
|||
|
|
```csharp
|
|||
|
|
foreach (int value in dict.Values)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine(value);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
注意:
|
|||
|
|
复习时不要把 `Dictionary` 当成“按下标顺序排列的列表”
|
|||
|
|
它的核心用途是通过 key 查 value
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# key 不能重复
|
|||
|
|
错误写法:
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
|||
|
|
|
|||
|
|
dict.Add("hp", 100);
|
|||
|
|
dict.Add("hp", 200);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
原因:
|
|||
|
|
`Dictionary` 中的 key 必须唯一
|
|||
|
|
重复添加同一个 key 会报错
|
|||
|
|
|
|||
|
|
如果想修改旧值:
|
|||
|
|
```csharp
|
|||
|
|
dict["hp"] = 200;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# key 一般不能为 null
|
|||
|
|
错误写法:
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
|||
|
|
|
|||
|
|
// dict.Add(null, 100);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
原因:
|
|||
|
|
`Dictionary` 的 key 不能为 `null`
|
|||
|
|
|
|||
|
|
补充:
|
|||
|
|
如果 `TValue` 是引用类型或可空类型
|
|||
|
|
value 可以是 `null`
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, string> dict = new Dictionary<string, string>();
|
|||
|
|
dict["name"] = null;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 和 Hashtable 的区别
|
|||
|
|
`Hashtable`:
|
|||
|
|
非泛型
|
|||
|
|
key 和 value 都按 `object` 处理
|
|||
|
|
取值时常常需要强制类型转换
|
|||
|
|
|
|||
|
|
`Dictionary<TKey, TValue>`:
|
|||
|
|
泛型
|
|||
|
|
key 和 value 类型明确
|
|||
|
|
编译期就能检查类型错误
|
|||
|
|
使用起来更安全,更常见
|
|||
|
|
|
|||
|
|
对比表:
|
|||
|
|
|
|||
|
|
| 对比点 | `Hashtable` | `Dictionary<TKey, TValue>` |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 类型 | 非泛型 | 泛型 |
|
|||
|
|
| 键和值 | `object` | 指定类型 |
|
|||
|
|
| 类型安全 | 较弱 | 更强 |
|
|||
|
|
| 取值 | 常要强转 | 一般不用强转 |
|
|||
|
|
| 推荐程度 | 主要用于学习旧集合 | 实际开发常用 |
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 和 List 的区别
|
|||
|
|
| 对比点 | `List<T>` | `Dictionary<TKey, TValue>` |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 存储方式 | 单个元素 | 键值对 |
|
|||
|
|
| 查找方式 | 常用下标 | 常用 key |
|
|||
|
|
| key | 没有 | 必须唯一 |
|
|||
|
|
| 适合场景 | 一组同类型数据 | 根据唯一键查数据 |
|
|||
|
|
|
|||
|
|
一句话:
|
|||
|
|
`List<T>` 更像“列表”
|
|||
|
|
`Dictionary<TKey, TValue>` 更像“字典”
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 基础案例:角色表
|
|||
|
|
```csharp
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
|
|||
|
|
class Player
|
|||
|
|
{
|
|||
|
|
public string Name;
|
|||
|
|
public int Level;
|
|||
|
|
|
|||
|
|
public Player(string name, int level)
|
|||
|
|
{
|
|||
|
|
Name = name;
|
|||
|
|
Level = level;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class Program
|
|||
|
|
{
|
|||
|
|
static void Main(string[] args)
|
|||
|
|
{
|
|||
|
|
Dictionary<int, Player> players = new Dictionary<int, Player>();
|
|||
|
|
|
|||
|
|
players.Add(1001, new Player("小明", 10));
|
|||
|
|
players.Add(1002, new Player("小红", 12));
|
|||
|
|
players.Add(1003, new Player("小刚", 8));
|
|||
|
|
|
|||
|
|
if (players.TryGetValue(1002, out Player player))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine(player.Name + " " + player.Level);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
输出:
|
|||
|
|
```text
|
|||
|
|
小红 12
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
理解:
|
|||
|
|
1. `1001`、`1002`、`1003` 是玩家编号
|
|||
|
|
2. 通过编号可以快速找到对应玩家对象
|
|||
|
|
3. 这种“编号 -> 对象”的场景非常适合用 `Dictionary`
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 常用成员
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
|||
|
|
|
|||
|
|
dict.Add("A", 1);
|
|||
|
|
dict["B"] = 2;
|
|||
|
|
|
|||
|
|
Console.WriteLine(dict.Count);
|
|||
|
|
Console.WriteLine(dict.ContainsKey("A"));
|
|||
|
|
Console.WriteLine(dict.ContainsValue(2));
|
|||
|
|
|
|||
|
|
dict.Remove("A");
|
|||
|
|
dict.Clear();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
常用方法 / 成员:
|
|||
|
|
1. `Add(TKey key, TValue value)`:添加键值对
|
|||
|
|
2. `Remove(TKey key)`:删除指定 key
|
|||
|
|
3. `Clear()`:清空
|
|||
|
|
4. `ContainsKey(TKey key)`:判断 key 是否存在
|
|||
|
|
5. `ContainsValue(TValue value)`:判断 value 是否存在
|
|||
|
|
6. `TryGetValue(TKey key, out TValue value)`:安全取值
|
|||
|
|
7. `Keys`:获取所有键
|
|||
|
|
8. `Values`:获取所有值
|
|||
|
|
9. `Count`:键值对数量
|
|||
|
|
10. `dict[key]`:通过 key 取值或赋值
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 注意
|
|||
|
|
1. `Dictionary<TKey, TValue>` 在 `System.Collections.Generic` 命名空间中
|
|||
|
|
2. 它是泛型键值对集合
|
|||
|
|
3. key 必须唯一
|
|||
|
|
4. `Add` 遇到重复 key 会报错
|
|||
|
|
5. `dict[key] = value` 可以新增,也可以覆盖旧值
|
|||
|
|
6. 用不存在的 key 直接取值可能报错
|
|||
|
|
7. 更稳的取值方式通常是 `TryGetValue`
|
|||
|
|
8. 它最核心的优势是“通过 key 快速找 value”
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 易错点
|
|||
|
|
1. 不要把 `Dictionary` 当数组或 `List<T>` 来用
|
|||
|
|
2. 不要默认 key 一定存在
|
|||
|
|
3. `ContainsKey` 判断的是键,不是值
|
|||
|
|
4. `ContainsValue` 可以用,但实际开发里更常关心 key
|
|||
|
|
5. 重复 `Add` 同一个 key 会报错
|
|||
|
|
|
|||
|
|
错误示例:
|
|||
|
|
```csharp
|
|||
|
|
Dictionary<string, int> dict = new Dictionary<string, int>();
|
|||
|
|
dict.Add("hp", 100);
|
|||
|
|
|
|||
|
|
// Console.WriteLine(dict["mp"]);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
原因:
|
|||
|
|
`"mp"` 这个 key 不存在
|
|||
|
|
直接取值可能报错
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 一句话记忆
|
|||
|
|
==Dictionary<TKey, TValue> 是通过唯一 key 快速查找 value 的泛型键值对集合。==
|
|||
|
|
|
|||
|
|
再压缩一点:
|
|||
|
|
==List 用下标找,Dictionary 用 key 找。==
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
# 面试/复习时怎么说
|
|||
|
|
可以这样答:
|
|||
|
|
|
|||
|
|
“`Dictionary<TKey, TValue>` 是 C# 中非常常用的泛型键值对集合,适合通过唯一 key 快速查找 value。
|
|||
|
|
它的 key 不能重复,常用操作有 `Add`、`Remove`、`ContainsKey` 和 `TryGetValue`。
|
|||
|
|
相比 `Hashtable`,`Dictionary` 类型更安全,不需要频繁强制类型转换,所以实际开发中更常用。”
|
|||
|
|
|
|||
|
|
----
|
|||
|
|
引用:
|
|||
|
|
1. Microsoft Learn: [Dictionary<TKey,TValue> Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-9.0)
|
|||
|
|
2. Microsoft Learn: [Dictionary<TKey,TValue>.TryGetValue Method](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetvalue?view=net-9.0)
|
|||
|
|
3. Microsoft Learn: [System.Collections.Generic Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic?view=net-9.0)
|
|||
|
|
|
|||
|
|
#Dictionary
|
|||
|
|
#字典
|
|||
|
|
#泛型集合
|
|||
|
|
#集合
|
|||
|
|
#进阶
|