CodeGym /课程 /C# SELF /字典: Dictionary<TKey, TV...

字典: Dictionary<TKey, TValue>

C# SELF
第 27 级 , 课程 3
可用

1. 入门

想象一下,你是个小店老板,有一份商品清单。每个商品都有独一无二的货号(比如ART-001ART-002),当然还有名字、价格、库存数量。

如果你把这些都放在List<T>里,T比如说是我们以后要写的Product类,那你要找货号ART-005的商品时,就得一个个翻:
“这是ART-001吗?不是。这是ART-002?也不是……哦,这个是ART-005!找到了!”
要是只有10个商品还好,要是有1万个、10万个呢?每次找都得花老半天。顾客等你找他最爱的饼干都要等到天荒地老了,这不行!

我们需要一种方法,只要知道唯一的货号就能立刻定位到商品,不用全都翻一遍。也就是说,我们需要一个“key”,能直接指向我们要的“value”。

认识Dictionary

这时候Dictionary<TKey, TValue>就闪亮登场啦!你可以把它想成超级智能的电话本。普通电话本是通过名字(key)找电话号码(value)。你翻到“A”,找到“阿乐”,电话号码就出来了。你不用把所有号码都翻一遍。

Dictionary(翻译过来就是“字典”)也是这样,它把数据存成“键-值”对

  • 键(TKey:每个元素的唯一标识。就像电话本里的名字或者商品的货号。你就是用key来找value的。key在同一个字典里必须唯一。如果你想加一个已经有的key,Dictionary会直接拒绝你。
  • 值(TValue:你想存的数据,比如电话号码、商品价格、术语解释,啥都行!

TKeyTValue这俩尖括号里的字母,意思是Dictionary泛型(Generic)集合。你自己决定key是什么类型,value是什么类型。key可以是string(名字、货号)、int(用户ID),甚至你自己写的类。value也可以是intstringdouble或者对象。

优点?极速访问!因为它内部有特殊结构(叫哈希表,别慌,先不用懂原理),Dictionary能在极短时间内通过key找到value,不管你有10个还是100万个元素。就像图书馆的超级索引:你说“我要C#的书”,马上就能告诉你在哪,不用一排排翻。

咱们直接上手吧!我们要做个互动的“C#术语小字典”,边学边记新概念。

2. 基本语法:创建字典

一切都很常规:声明变量,不过这次要写两个类型——key的类型(TKey)和value的类型(TValue):

// 简单字典:key是string(登录名),value是string(email)
Dictionary<string, string> userEmails = new Dictionary<string, string>();

// 或者用var更简洁
var userEmails = new Dictionary<string, string>();

为啥必须写两个类型?
因为C#是强类型语言,字典得知道你给它准备了什么类型的key和value。

添加元素

要加新的“键-值”对,用Add方法。key必须唯一!

userEmails.Add("user1", "user1@example.com");
userEmails.Add("user2", "user2@gmail.com");

如果你用同一个key再加一次,字典会生气,直接抛异常。

通过key访问value

字典最酷的地方就是可以直接通过key拿到value:

string email = userEmails["user1"];
Console.WriteLine(email); // user1@example.com

如果你用不存在的key,程序会立刻报错(抛KeyNotFoundException异常)。后面我们会学怎么安全地检查key。

通过key修改value

如果key已经有了,直接赋新值就行:

userEmails["user1"] = "user1@newmail.ru"; // 现在Vasya的email变了

如果key原来没有,这样赋值会新建一个元素。

用户示例

我们来升级一下我们的学习小程序。之前我们有个任务列表(List<string> tasks;)做ToDo程序。现在想加个“登录”功能:每个用户要有email。

可以这样写:

// UserId是string,Email也是string
var users = new Dictionary<string, string>();
users.Add("admin", "admin@myapp.com");
users.Add("alice", "alice@wonderland.com");
users.Add("bob", "bob@builder.com");

现在你随时可以通过登录名快速查到用户的email:

Console.WriteLine(users["alice"]); // => alice@wonderland.com

3. Dictionary的常用方法和属性

方法/属性 说明
Add(key, value)
添加新的“键-值”对。
Remove(key)
通过key删除元素。
ContainsKey(key)
检查有没有这个key。
ContainsValue(value)
检查有没有这个value(很慢!)。
TryGetValue(key, out val)
安全地通过key获取value,不会抛异常。
Count
字典里“键-值”对的数量。
Keys
所有key的集合。
Values
所有value的集合。

检查key是否存在

最常见(也最安全)的做法——先检查key在不在:

if (users.ContainsKey("user3"))
{
    Console.WriteLine(users["user3"]);
}
else
{
    Console.WriteLine("用户user3没找到!");
}

更安全的方式:TryGetValue

TryGetValue方法能避免异常:

if (users.TryGetValue("bob", out string email))
{
    Console.WriteLine($"Bob的邮箱:{email}");
}
else
{
    Console.WriteLine("Bob没找到!");
}

这是个好习惯,面试也常考——早点学会!而且这个方法比ContainsKey+索引快。

4. 遍历字典:foreach循环

如果你要遍历所有“键-值”对,用foreach循环。字典里的每个元素其实是KeyValuePair<TKey, TValue>对象:

foreach (var pair in users)
{
    Console.WriteLine($"登录名: {pair.Key}, Email: {pair.Value}");
}

或者你想更潮一点:

foreach (var (login, email) in users)
{
    Console.WriteLine($"{login}: {email}");
}
// 这种写法是元组解构(C# 7+)带来的。

5. 删除和修改value

要通过登录名删除用户很简单:

users.Remove("alice");

如果没有这个key,会返回false。放心删,不会抛异常。

修改用户的email:

users["bob"] = "bob@constructor.com";

如果key原来没有,会新加一对!

6. 字典的Keys和Values属性

如果你只想要所有登录名(key)或者所有email(value),用KeysValues集合:

foreach (string login in users.Keys)
{
    Console.WriteLine("登录名: " + login);
}

foreach (string email in users.Values)
{
    Console.WriteLine("Email: " + email);
}

7. 字典的重要细节

key必须唯一

也就是说不能加两个一样的key。否则会抛异常。这样保证了数据安全:一个用户不能有两个email(每条记录只能有一个)。

key不能是null(对于string

如果你用字符串key,试图加null会报错(ArgumentNullException)。如果你发现没key,可能是数据逻辑有问题。

为啥字典查找这么快?

Dictionary底层是哈希表。也就是说查找key不是一个个比,而是用特殊的“哈希函数”算出位置,几乎直接就能拿到value。

什么能当key用?

  • 任何能正确判断相等、能生成唯一哈希码的类型(要有EqualsGetHashCode()方法)。
  • 通常用stringintGuid或者你自己的类型(但自定义类型要小心重写Equals/GetHashCode,不然会有奇怪bug)。

8. 把字典加到应用里

在我们的ToDo小程序里,我们加个用户字典,实现通过登录名查email并处理错误的功能:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 用户字典:登录名 => email
        var users = new Dictionary<string, string>
        {
            { "admin", "admin@myapp.com" },
            { "alice", "alice@wonderland.com" },
            { "bob", "bob@builder.com" }
        };

        Console.WriteLine("请输入要查找email的用户登录名:");
        string login = Console.ReadLine();

        // 安全查找email
        if (users.TryGetValue(login, out string email))
        {
            Console.WriteLine($"用户{login}的Email: {email}");
        }
        else
        {
            Console.WriteLine($"用户{login}没找到。");
        }

        // 遍历所有用户
        Console.WriteLine("\n所有用户列表:");
        foreach (var pair in users)
        {
            Console.WriteLine($"{pair.Key} => {pair.Value}");
        }
    }
}

9. 新手常见错误和坑

有时候很想这样写:

// 以为key不存在也没事
string value = users["nonexistent"]; // 崩了!KeyNotFoundException!

记住:如果不确定key一定存在,先用ContainsKeyTryGetValue检查。

还有,遍历value不能保证它们唯一!两个登录名可能有同一个email(如果你没控制好value的唯一性)。

方法也容易搞混——比如想通过value删:

users.Remove("bob@builder.com"); // 删不掉!它要的是key,不是value。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION