【C#零基础从入门到精通】(十九)——C#字典详解
在 C# 中,字典是一种非常实用的数据结构,用于存储键值对(Key - Value Pairs),它允许通过键(Key)快速查找对应的值(Value)。C# 中主要使用 Dictionary<TKey, TValue>
泛型类来实现字典功能,下面将从多个方面详细介绍 C# 字典。
1. 引入命名空间
在使用 Dictionary<TKey, TValue>
之前,需要引入 System.Collections.Generic
命名空间,示例代码如下:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 后续使用字典的代码将在这里编写
}
}
2. 创建字典对象
可以使用无参构造函数或带有初始容量参数的构造函数来创建字典对象,也可以在创建时直接初始化一些键值对。
// 使用无参构造函数创建字典
Dictionary<string, int> dict1 = new Dictionary<string, int>();
// 使用带有初始容量参数的构造函数创建字典
Dictionary<string, int> dict2 = new Dictionary<string, int>(10);
// 创建字典并初始化一些键值对
Dictionary<string, int> dict3 = new Dictionary<string, int>
{
{ "apple", 1 },
{ "banana", 2 },
{ "cherry", 3 }
};
3. 添加键值对
可以使用 Add
方法或直接通过键来添加键值对。
Dictionary<string, int> fruitDict = new Dictionary<string, int>();
// 使用 Add 方法添加键值对
fruitDict.Add("apple", 1);
fruitDict.Add("banana", 2);
// 直接通过键来添加键值对
fruitDict["cherry"] = 3;
需要注意的是,如果使用 Add
方法添加已经存在的键,会抛出 ArgumentException
异常;而直接通过键赋值,如果键已存在则会更新该键对应的值。
4. 访问值
可以通过键来访问字典中对应的值。
Dictionary<string, int> numberDict = new Dictionary<string, int>
{
{ "one", 1 },
{ "two", 2 },
{ "three", 3 }
};
// 通过键访问值
int value = numberDict["two"];
Console.WriteLine($"The value of 'two' is: {value}");
如果尝试访问不存在的键,会抛出 KeyNotFoundException
异常。为了避免这种情况,可以使用 ContainsKey
方法先检查键是否存在。
if (numberDict.ContainsKey("four"))
{
int val = numberDict["four"];
Console.WriteLine($"The value of 'four' is: {val}");
}
else
{
Console.WriteLine("The key 'four' does not exist.");
}
另外,还可以使用 TryGetValue
方法来安全地获取值。
int result;
if (numberDict.TryGetValue("three", out result))
{
Console.WriteLine($"The value of 'three' is: {result}");
}
else
{
Console.WriteLine("The key does not exist.");
}
5. 修改值
可以直接通过键来修改字典中对应的值。
Dictionary<string, int> scoreDict = new Dictionary<string, int>
{
{ "Alice", 80 },
{ "Bob", 90 }
};
// 修改键 "Alice" 对应的值
scoreDict["Alice"] = 85;
Console.WriteLine($"Alice's new score is: {scoreDict["Alice"]}");
6. 删除键值对
可以使用 Remove
方法来删除指定键的键值对。
Dictionary<string, int> colorDict = new Dictionary<string, int>
{
{ "red", 1 },
{ "green", 2 },
{ "blue", 3 }
};
// 删除键 "green" 对应的键值对
bool removed = colorDict.Remove("green");
if (removed)
{
Console.WriteLine("The key 'green' and its value have been removed.");
}
else
{
Console.WriteLine("The key 'green' does not exist.");
}
7. 检查键或值是否存在
- 检查键是否存在:使用
ContainsKey
方法。
Dictionary<string, int> animalDict = new Dictionary<string, int>
{
{ "dog", 1 },
{ "cat", 2 }
};
bool hasDog = animalDict.ContainsKey("dog");
if (hasDog)
{
Console.WriteLine("The key 'dog' exists.");
}
- 检查值是否存在:使用
ContainsValue
方法。
bool hasValue2 = animalDict.ContainsValue(2);
if (hasValue2)
{
Console.WriteLine("The value 2 exists.");
}
8. 遍历字典
可以使用 foreach
循环来遍历字典中的键值对、键或值。
- 遍历键值对:字典中的每个元素是一个
KeyValuePair<TKey, TValue>
类型的对象。
Dictionary<string, int> planetDict = new Dictionary<string, int>
{
{ "Earth", 1 },
{ "Mars", 2 },
{ "Venus", 3 }
};
foreach (KeyValuePair<string, int> pair in planetDict)
{
Console.WriteLine($"Key: {pair.Key}, Value: {pair.Value}");
}
- 遍历键:可以使用
Keys
属性获取字典中所有的键。
foreach (string key in planetDict.Keys)
{
Console.WriteLine($"Key: {key}");
}
- 遍历值:可以使用
Values
属性获取字典中所有的值。
foreach (int value in planetDict.Values)
{
Console.WriteLine($"Value: {value}");
}
9. 获取字典的元素数量
可以使用 Count
属性来获取字典中键值对的数量。
Dictionary<string, int> flowerDict = new Dictionary<string, int>
{
{ "rose", 1 },
{ "lily", 2 },
{ "tulip", 3 }
};
int count = flowerDict.Count;
Console.WriteLine($"The dictionary contains {count} key - value pairs.");
10. 字典的性能考虑
- 哈希冲突:当不同的键产生相同的哈希码时,会发生哈希冲突。字典通过链表等方式解决哈希冲突,这可能会影响查找性能。为了减少哈希冲突,键的类型应该正确实现
GetHashCode
和Equals
方法。 - 扩容:当字典中的元素数量超过其容量时,字典会自动扩容,这涉及到重新分配内存和重新计算哈希码,会带来一定的性能开销。因此,如果能预估字典的大致容量,可以在初始化时指定合适的容量。
11. 其他相关字典类
SortedDictionary<TKey, TValue>
:该类同样存储键值对,但会根据键的顺序对元素进行排序。它使用红黑树实现,查找、插入和删除操作的时间复杂度为 \(O(log n)\)。
SortedDictionary<string, int> sortedScores = new SortedDictionary<string, int>
{
{ "Alice", 85 },
{ "Bob", 90 },
{ "Charlie", 78 }
};
SortedList<TKey, TValue>
:也是一个有序的键值对集合,它结合了数组和二分查找,在内存使用上更高效,但插入和删除操作的性能相对较低。
12. 应用场景
12.1 缓存数据
当需要频繁访问一些数据,但这些数据的获取成本较高(如从数据库或网络获取)时,可以使用字典作为缓存。将数据的唯一标识作为键,数据本身作为值存储在字典中。
Dictionary<int, User> userCache = new Dictionary<int, User>();
public User GetUser(int userId)
{
if (userCache.ContainsKey(userId))
{
return userCache[userId];
}
// 从数据库或其他数据源获取用户信息
User user = FetchUserFromDatabase(userId);
userCache[userId] = user;
return user;
}
12.2 统计数据
可以使用字典来统计数据的出现次数,例如统计文本中每个单词的出现频率。
string text = "hello world hello";
string[] words = text.Split(' ');
Dictionary<string, int> wordCount = new Dictionary<string, int>();
foreach (string word in words)
{
if (wordCount.ContainsKey(word))
{
wordCount[word]++;
}
else
{
wordCount[word] = 1;
}
}
foreach (KeyValuePair<string, int> pair in wordCount)
{
Console.WriteLine($"Word: {pair.Key}, Count: {pair.Value}");
}
12.3 配置管理
在应用程序中,经常需要存储和管理各种配置信息。可以使用字典将配置项的名称作为键,配置值作为值进行存储和访问。
Dictionary<string, string> config = new Dictionary<string, string>
{
{ "DatabaseConnectionString", "Server=localhost;Database=MyDB;User=sa;Password=123456" },
{ "AppName", "MyApplication" }
};
string connectionString = config["DatabaseConnectionString"];
12.4 路由映射
在 Web 开发中,路由映射是将 URL 路径映射到相应的处理程序或页面。可以使用字典来实现这种映射关系。
Dictionary<string, Action> routeMap = new Dictionary<string, Action>
{
{ "/home", () => Console.WriteLine("Home page") },
{ "/about", () => Console.WriteLine("About page") }
};
string currentUrl = "/home";
if (routeMap.ContainsKey(currentUrl))
{
routeMap[currentUrl]();
}
13. 字典的优缺点
优点
- 快速查找:字典使用哈希表实现,通过键查找值的时间复杂度接近 \(O(1)\),在大量数据的查找场景中效率很高。
- 灵活性:可以使用不同类型的键和值,只要键的类型实现了
GetHashCode
和Equals
方法。
缺点
- 内存开销:字典需要额外的内存来维护哈希表,相比于简单的数组或列表,内存占用可能会更大。
- 无序性:字典中的键值对是无序的,不能保证元素的插入顺序或其他特定顺序。如果需要有序的键值对集合,可以考虑使用
SortedDictionary<TKey, TValue>
或SortedList<TKey, TValue>
。
综上所述,C# 中的 Dictionary<TKey, TValue>
是一个强大且实用的数据结构,适用于需要快速查找和存储键值对的场景。