字典
C# 中的 A 是一个泛型集合,可用于存储键/值对,其中每个键在字典中是唯一的,并且它映射到特定值。Dictionary 类是作为哈希表实现的,因此提供了对基于键的值的快速访问。打个比方,你可以把字典想象成一个现实世界的字典,你在其中查找一个单词(键)来找到它的定义(值)。Dictionary
当您将键值对添加到字典时,它会根据键计算哈希码,该哈希码用于确定键值对在字典中的存储位置。正因为如此,使用 Key in a Dictionary 检索值的速度非常快——接近 O(1)。
Dictionary<TKey, TValue>class 允许您为键 () 和值 () 指定数据类型,例如,您可以创建一个整数字典作为键,将字符串创建为值(或任何其他数据类型),即:。Dictionary 键必须是唯一的,并且不应为 null(对于引用类型),而值可以为 null(如果它是引用类型)。TKeyTValueDictionary<int, string>
当您需要拥有集合并需要使用某些键(类)执行搜索时,字典是合适的。它可用于自定义数据结构、配置设置等任务,其中每个设置都由唯一键标识,用于将数据临时存储在内存中以供快速检索的简单缓存,需要通过唯一键快速检索的数据存储,本地化,其中每个键代表一种语言中的术语或短语,并映射到另一种语言的相关翻译, 错误**处理和日志记录,**用于存储错误代码或异常详细信息以及相应的错误消息)。Dictionary<TKey, TValue>
字典示例
出于演示目的,我使用 .NET 7 创建了一个控制台应用程序,并将提供一些编码示例,说明如何在 C# 中使用字典。本例源代码在文末提供下载。
如何声明和初始化字典
为了便于演示,我将使用一个支付方式字典作为示例,该字典接收 a 作为 ,a 接收为 :integerKeystringValue
Dictionary<int, string> paymentMethods = new Dictionary<int, string>();
如何将键/值对添加到字典
为了将键值数据添加到字典中,您可以使用以下方法:Add
paymentMethods.Add(1, "Credit Card");
paymentMethods.Add(2, "PayPal");
paymentMethods.Add(3, "Google Pay");
如何安全地将键/值对添加到字典
如果尝试使用现有键添加其他项,则会引发错误。例如,使用前面添加 3 个项目的示例,如果我们尝试使用现有键添加新项目,将抛出以下错误:ArgumentException
paymentMethods.Add(3, "Cash");
输出将是一个例外:
Unhandled exception. System.ArgumentException: An item with the same key has already been added. Key: 3
为了避免这种情况,您可以使用该方法。如果字典中不存在键,此方法将添加该项,如果存在,则不会执行任何操作,也不会引发错误。例如:TryAdd
var cashAdded = paymentMethods.TryAdd(3, "Cash");
var criptoCointAdded = paymentMethods.TryAdd(4, "Bitcoin");
Console.WriteLine($"Was Cash added? {cashAdded}");
Console.WriteLine($"Was Bitcoin added? {criptoCointAdded}");
输出将为:
Was Cash added? False
Was Bitcoin added? True
如何在字典中使用键和值进行迭代
若要检查字典中的项目,可以使用循环指令(或)来访问每个项目。例如:foreachLINQ
foreach (var paymentMethod in paymentMethods)
{
Console.WriteLine($"Key: {paymentMethod.Key}, Value: {paymentMethod.Value}");
}
输出将为:
Key: 1, Value: Credit Card
Key: 2, Value: PayPal
Key: 3, Value: Google Pay
Key: 4, Value: Bitcoin
如何专门循环访问键和值
如果只想使用键或仅使用值进行专门迭代,则可以使用 或 指定,例如:.Keys.Values
var keys = paymentMethods.Keys;
foreach (var item in keys)
{
Console.WriteLine($"{item}");
}
与 Values 类似:
var values = paymentMethods.Values;
foreach (var item in values)
{
Console.WriteLine($"{item}");
}
输出将为:
1
2
3
4
Credit Card
PayPal
Google Pay
Bitcoin
如何检索按键搜索的目标值
现在,假设您需要返回 Dictionary 中 Key number 2 中项目的目标值,为此您需要指定 Dictionary 和 Key 之间的 ,例如:[]
var paymentMethod = paymentMethods[keyToSearch];
Console.WriteLine($"Payment Method with key {keyToSearch}: {paymentMethod}");
执行时,将具有值 ,因为这是包含此字典中键号 2 的项。这将是输出:
paymentMethodPayPal Payment Method with key 2: PayPal
当密钥不存在时会发生什么情况?
当您搜索字典中不存在的键时,将抛出 a。例如,下面的代码将导致以下错误消息:KeyNotFoundException
var paymentMethod = paymentMethods[5];
此操作的输出将为:
System.Collections.Generic.KeyNotFoundException: The given key '4' was not present in the dictionary.
如何按目标值检索键搜索
在字典中,您不能通过按目标值搜索来直接检索键,但是,您可以通过遍历字典并比较值来执行这样的搜索。在下面的示例中,使用 LINQ 完成了筛选,该筛选基于值筛选 Dictionary,然后选择包含该特定值的所有 Key。此搜索的结果将添加到变量中:List<int>
var valueToSearch = "PayPal";
// Find keys by searching by the target value:
var keysWithValue = paymentMethods
.Where(kv => kv.Value == valueToSearch)
.Select(kv => kv.Key)
.ToList();
foreach (var key in keysWithValue)
{
Console.WriteLine($"Key with value {valueToSearch}: {key}");
}
输出将为:
Key with value PayPal: 2
请注意,在此示例中,结果可能是多个项目,因为 不是唯一的。如果您有多个特定值的项目,并且想要返回第一个项目,则可以使用 代替 .ValueFirstOrDefault()ToList()
如何安全地通过键进行目标值搜索
如果要搜索值并希望避免引发错误,可以使用以下方法:KeyNotFoundExceptionTryGetValue
if (paymentMethods.TryGetValue(keyToSearch, out string resultValue))
{
Console.WriteLine($"Key {keyToSearch} was found. The target value is: {resultValue}");
}
else
{
Console.WriteLine($"The Key {keyToSearch} was not found.");
}
输出将为:
// For a non existent key:
The Key 5 was not found.
// For an existent key:
Key 4 was found. The target value is: Bitcoin
使用 LINQ 进行搜索
也可以利用 在字典中执行搜索。如果密钥不存在,则不会引发异常。LINQ
在下面的示例中,您可以了解如何使用 LINQ 按特定键进行搜索。
var paymentMethod = paymentMethods
.Where(pair => pair.Key == keyToSearch)
.Select(pair => pair.Value)
.FirstOrDefault();
输出将为:
// For an existent key:
Payment method 2: PayPal
// For a non existent key:
Payment method 6 was not found
您也可以执行类似操作以按目标值进行搜索,在这种情况下,可能会返回多个结果(因为与键不同,目标值不是唯一的)。例如:
var paymentMethod = paymentMethods
.Where(pair => pair.Key == keyToSearch)
.Select(pair => pair.Value)
.FirstOrDefault();
if (paymentMethod != null)
{
Console.WriteLine($"Payment method {keyToSearch}: {paymentMethod}");
}
else
{
Console.WriteLine($"Payment method {keyToSearch} was not found");
}
输出将为:
// When more than one key-value pair with target value "PayPal" exists:
Payment method(s) named 'PayPal' found with key(s): 2, 5
// When target value was not found in the dictionary:
Payment method 'Cash' was not found
检查是否存在包含的键或值
如果需要检查字典中是否存在 Key 或 Value,可以使用返回布尔值的 和 方法:ContainsKeyContainsValue
var containsKey1 = paymentMethods.ContainsKey(1);
var containsKey5 = paymentMethods.ContainsKey(5);
Console.WriteLine($"Contains Key 1: {containsKey1}");
Console.WriteLine($"Contains Key 5: {containsKey5}");
var containsValuePayPal = paymentMethods.ContainsValue("PayPal");
var containsValueCash = paymentMethods.ContainsValue("Cash");
Console.WriteLine($"Contains Value PayPal: {containsValuePayPal}");
Console.WriteLine($"Contains Value Cash: {containsValueCash}");
输出将为:
Contains Key 1: True
Contains Key 5: False
Contains Value PayPal: True
Contains Value Cash: False
如何更新字典中键的值?
假设现在我们需要更新字典中 Key number 3 的值,您可以通过传递 Key 和 new Value 轻松更新它:
var key = 3;
Console.WriteLine($"Value of Key 3 before the update: {paymentMethods[key]}");
// Update the value of the item with Key number 3:
paymentMethods[key] = "Apple Pay";
Console.WriteLine($"Value of Key {key} after the update: {paymentMethods[key]}");
输出将为:
Value of Key 3 before the update: Google Pay
Value of Key 3 after the update: Apple Pay
如何更新字典中的键?
字典中的键是不可变的(因为它们用于创建哈希值,从而确保有效的查找和检索),这意味着我们无法更新现有的键。但是,您可以通过添加新的键值对并删除前一个键值对来实现类似的“更新”效果:
var keyToBeRecriated = 3;
var newKeyToBeCreated = 5;
if (paymentMethods.ContainsKey(keyToBeRecriated))
{
// Step 1: Add a new key-value pair with the updated key:
paymentMethods[newKeyToBeCreated] = paymentMethods[keyToBeRecriated];
// Step 2: Remove the old key:
paymentMethods.Remove(keyToBeRecriated);
}
// Display the updated dictionary
foreach (var paymentMethod in paymentMethods)
{
Console.WriteLine($"{paymentMethod.Key}: {paymentMethod.Value}");
}
在此示例中,删除了密钥编号为 3 的项目(“Apple Pay”),并在字典中添加了一个新项目,密钥编号为 4,值为“Apple Pay”。输出将为:
Key: 1, Value: Credit Card
Key: 2, Value: PayPal
Key: 4, Value: Apple Pay
如何检查字典中存在多少个项目
如果需要检查字典中的项数,可以使用以下方法:CountLINQ
var dictionarySize = paymentMethods.Count();
Console.WriteLine($"Dictionary size: {dictionarySize}");
输出将为:
Dictionary size: 4
如何删除词典中的项目
要删除字典中的项目,我们可以使用 .如果 Key 存在于字典中,此方法将删除键值对,如果该键不存在,则它不会执行任何操作。Remove
// Remove an item based on the Key:
paymentMethods.Remove(keyToBeRemoved);
// or
// Which returns a boolean value (true when item is removed, and false when is not removed):
var wasRemoved = paymentMethods.Remove(keyToBeRemoved);
ConcurrentDictionary (并发词典)
A 是键/值对的线程安全集合,可由多个线程并发访问,并确保数据完整性。此类旨在处理来自多个线程的并发访问,从而避免争用条件 — 当两个或多个线程同时访问和修改共享数据或资源时,可能会出现争用条件,从而导致不可预知的行为,例如数据损坏、不正确的结果、异常等。当您需要可由多个线程同时访问的键/值对的线程安全集合时,可以使用该类。ConcurrentDictionaryConcurrentDictionary<TKey,TValue>
“通过 ConcurrentDictionary<TKey,TValue> 实现的接口之一访问的成员(包括扩展方法)不能保证是线程安全的,可能需要由调用方同步。”(Microsoft 文档)
您可以像这样初始化 ConcurrentDictionary:
ConcurrentDictionary<int, string> paymentMethodsConcurrentDictionary = new ConcurrentDictionary<int, string>();
如何在 ConcurrentDictionary 中添加项
与将项添加到 Dictionary 中类似,若要将项添加到 ConcurrentDictionary 中,可以使用该方法。TryAdd
如何在 ConcurrentDictionary 中搜索项目
要在 ConcurrentDictionary 中获取键值对,您可以使用 .此方法将根据 Key 搜索项目,并将返回一个布尔值(找到项目时为 true,未找到项目时为 false),而不会在密钥不存在时引发异常。例如:TryGetMethod
var existentKey = 2;
if (paymentMethodsConcurrentDictionary.TryGetValue(existentKey, out string valueForExistingKey))
{
Console.WriteLine($"Key {existentKey} was found. Value: {valueForExistingKey}");
}
else
{
Console.WriteLine($"Key {existentKey} was not found");
}
var nonExistentKey = 6;
if (paymentMethodsConcurrentDictionary.TryGetValue(nonExistentKey, out string valueForNonExistingKey))
{
Console.WriteLine($"Updated Value for {nonExistentKey}: {valueForNonExistingKey}");
}
else
{
Console.WriteLine($"Key {nonExistentKey} was not found\\n");
}
输出将为:
The Key 2 was removed. Value: PayPal
The Key 6 was not found
如果需要搜索目标值,可以按照与 Dictionary 示例相同的方法使用 。LINQ
如何在 ConcurrentDictionary 中添加或更新项
要添加新的键值对或更新现有项的目标值,可以使用该方法或。该方法的工作方式与之前介绍的普通字典类似,因此我将重点讨论该方法的这个主题。此方法接收三个参数:TryAddAddOrUpdateTryAddAddOrUpdate
这是如何使用该方法的示例。在第一个代码块中,将更新一个现有值,在第二个代码块中,将添加一个新的键值对:AddOrUpdate
// In this example, the key-value pair with Key 3 will be updated:
var keyToBeUpdated = 3;
paymentMethodsConcurrentDictionary.AddOrUpdate(
keyToBeUpdated,
"Apple Pay (Added)", // Value to add if the the key does not exist;
(key, oldValue) => "Apple Pay (Updated)" // Update the existing value;
);
// In this example, a new key-value pair with Key 4 will be added:
var keyToBeAdded = 4;
paymentMethodsConcurrentDictionary.AddOrUpdate(
keyToBeAdded,
"Cash (Added)", // Value to add since the key does not exist;
(key, oldValue) => oldValue // No update is performed in this case;
);
// Display the updated dictionary:
foreach (var keyValuePair in paymentMethodsConcurrentDictionary)
{
Console.WriteLine($"Key: {keyValuePair.Key}, Value: {keyValuePair.Value}");
}
输出将为:
Key: 1, Value: Credit Card
Key: 2, Value: PayPal
Key: 3, Value: Apple Pay (Updated)
Key: 4, Value: Cash (Added)
如何删除 ConcurrentDictionary 中的键值对
为了删除 ConcurrentDictionary 中的项目,您可以使用:TryRemove
var keyRemoved = paymentMethodsConcurrentDictionary.TryRemove(keyToBeRemoved, out string removedValue);
if (keyRemoved)
{
Console.WriteLine($"The Key {keyToBeRemoved} was removed. Value: {removedValue}");
}
else
{
Console.WriteLine($"The Key {keyToBeRemoved} was not found.");
}
输出将为:
// For an existent key:
The Key 2 was removed. Value: PayPal
// For a non existent key:
The Key 6 was not found.
结论
A 是 C# 中一种高效的数据结构,它提供了一种灵活而强大的方法来高效地管理键值对。Dictionary 类是作为哈希表实现的,允许您快速访问基于键的值。您可以将其用于单线程方案和多线程方案,从而避免争用条件。本文介绍了如何创建和使用字典,并使用此数据结构执行一系列操作。DictionaryDictionaryConcurrentDictionary
源代码获取:公众号回复消息【code:82994】