本文将介绍Dictionary的常见方法,并深入了解内部实现原理,包括哈希表、扩容机制和性能优化等。同时,还将讨论Dictionary在多线程环境下使用时的注意事项和解决方案,并提供一些性能调优的建议。 ### 目的:
通过本文,读者能够根据实际需求选择合适的数据结构。包括如何优化性能、避免常见错误等。 ## 二、Dictionary的基本概念 ### 定义: Dictionary<TKey, TValue>是一个泛型集合,用于存储键值对,其中键是唯一的。 1. 命名空间:**System.Collections.Generic**,在DotNet Framework 2.0中随着泛型集合的引入而出现。 2. 泛型类型:支持为不同的数据类型创建Dictionary实例,无需进行类型转换,装箱/拆箱,代码重用性搞。 ```C#
static void Main()
{
// 为不同的数据类型创建Dictionary实例
Dictionary<string, Person> people = new Dictionary<string, Person>();
people.Add("Alice", new Person { Name = "Alice", Age = 30 });
} ``` 3. 唯一键:每个键在Dictionary中是唯一的,不允许重复;如果尝试添加一个已经存在的键,则会抛出异常。 ```C#
static void Main()
{
Dictionary<string, int> ages = new Dictionary<string, int>();
ages.Add("Alice", 30);
try
{
ages.Add("Alice", 31); // 尝试添加重复的键
}
catch (ArgumentException){ }
} ``` 4. 删除操作 ```C# Dictionary<int, string> dict = new Dictionary<int, string>(); dict.Add(1, "one"); dict.Add(2, "two"); dict.Add(3, "three");
// 移除键为2的键值对 dict.Remove(2);
// 移除键为1的键值对 if (dict.ContainsKey(1)) { dict.Remove(1); }
5. 键值对:Dictionary中的每个元素都是一个键值对,由**Key-Value**组成。
```C#
static void Main()
{
Dictionary<string, int> ages = new Dictionary<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};
// 遍历键值对
foreach (KeyValuePair<string, int> kvp in ages)
{
Console.WriteLine($"Key = {kvp.Key}, Value = {kvp.Value}");
}
}
static void Main()
{
Dictionary<string, int> ages = new Dictionary<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};
// 通过Keys和Values属性分别遍历
Console.WriteLine("Keys:");
foreach (string key in ages.Keys)
{
Console.WriteLine(key);
}
Console.WriteLine("\nValues:");
foreach (int value in ages.Values)
{
Console.WriteLine(value);
}
}
static void Main()
{
// 指定初始容量
Dictionary<string, int> largeCapacityDictionary = new Dictionary<string, int>(1000);
// 注意:负载因子是内部管理的,不需要(也不允许)手动设置!!!
// 但可以通过指定大容量来减少扩容次数,从而提高性能。
}
在Dictionary中,查找操作是通过键的哈希码来快速定位到对应的数组索引,然后遍历链表(或在.NET Core 3.0及以上版本中的红黑树)以找到匹配的键值对。这个过程可以分为以下几个步骤:
删除操作:讨论删除指定键的键值对时,如何找到并移除该键值对,并处理可能的链表(或红黑树)调整。
Hash的背景
—
—
哈希函数是Hash的核心组成部分,它的作用是将任意长度的Key(如字符串、数字等)转化为固定长度的输出,这个输出被称为哈希值或哈希码,通常表示为一系列的字符或数字。
哈希函数的选择对于哈希表的性能至关重要。一个理想的哈希函数应该能够将输入数据均匀地映射到哈希表的各个位置,以最小化哈希冲突。
—
指的是不同的键(Key)经过哈希函数处理后产生了相同的哈希码(Hash Code);
简易ASSCI加法哈希函数的冲突解决;
什么是红黑树
—
红黑树的图例
注:红黑树是一种自平衡二叉查找树,它通过在插入和删除节点时调整树的平衡来保持时间复杂度为O(logN)。
红黑树的特性
— 错误的红黑树示例
— 错误的红黑树示例图解
— 红黑树的基本操作之旋转
![]()
— 红黑树的基本操作之插入
— 红黑树的基本操作之删除
![]()
—
五、Dictionary的性能优化
线程安全
Dictionary在C#中不是线程安全的。这意味着在多线程环境下,如果有多个线程同时对Dictionary进行读写操作,就可能导致数据不一致、损坏或其他不可预测的行为。为了避免这些问题,开发者需要采取额外的同步措施来确保线程安全。 解决方案: