前言:
在 C# 中,键值对是一种常见的数据结构,可以使用不同的集合类实现。以下是常用的键值对集合类:
-
Dictionary<TKey, TValue>:一种使用哈希表实现的键值对集合。它通过将键哈希为桶号,然后将值存储在桶中进行快速查找。
-
SortedList<TKey, TValue>:一种基于数组实现的键值对集合。它会将键值对按照键排序并存储在数组中,以支持快速访问、查找和枚举。
-
SortedDictionary<TKey, TValue>:一种使用红黑树实现的键值对集合。它能够按照键的排序进行快速查找,也可以快速地插入和删除键值对,并且该树具备自平衡的特性,使得插入、删除和搜索性能都非常优秀。
-
ConcurrentDictionary<TKey, TValue>:一种线程安全的键值对集合,它支持在高并发场景下的快速查找、插入和删除,并且保证线程安全。
-
ImmutableDictionary<TKey, TValue>:一种不可变的键值对集合,它提供了对集合进行修改时创建新集合的效率优化,同时也是线程安全的。
-
KeyValuePair<TKey, TValue>:一个用于表示键值对的结构体类型。它将一个键和一个值组合在一起,并提供了多种方法来处理这个组合。
在工作中比较经常用到Dictionary 和SortedList。
SortedList
var list = new SortedList<string, int>();
//使用[]方式添加键值对时若该键不参加就添加,存在就修改对应的值
list["芋泥波波"] = 16;
list["黄桃果霸"] = 16;
//使用Add添加键值对时,键不能重复,否则报错
("杨枝甘露", 19);
("满杯烧仙草", 21);
遍历获取键值,元素被检索为keyvaluePair对象
foreach (var VARIABLE in list)
{
( + " 价格:" + );
}
//输出:
黄桃果霸 价格:16
满杯烧仙草 价格:21
杨枝甘露 价格:19
芋泥波波 价格:16
获取值
//通过键获取值
("杨枝甘露的价格:" + list["杨枝甘露"]);
//通过索引获取值
("芋泥波波的价格:" + [0]);
//输出:
杨枝甘露的价格:19
芋泥波波的价格:16
//获取集合中的Values列表(若要获取所有的键则Values改成Keys)
IList<int> values = ;
foreach (var value in values)
{
(value);
}
输出:
16
21
19
16
如果在通过键获取值时,不确定该键值对是否存在,那么就使用TryGetValue,这样能避免键不存在时报异常
("黑糖珍珠", out int result);
(result);
输出:
0
判断
//判断是否包含某个键
if (("芋泥波波"))
{
("包含");
}
else
{
("不包含");
}
输出:
包含
移除
//通过键移除元素
("芋泥波波");
HashTable
var hashtable = new Hashtable();
("杨枝甘露", 19);
(21,"满杯烧仙草");
可以看出,使用HashTable时不需要设置类型,因此它是非泛型的
-
缺点:非泛型,需要装箱拆箱,效率较低,类型非安全,
-
好处:线程安全,当线程并发时,只有一个线程会执行这段代码
字典Dictionary
Dictionary与hashtable相反
-
缺点:线程非安全
-
优点:类型安全,效率高
IDictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary["芋泥波波"] = 16;
dictionary["黄桃果霸"] = 16;
("杨枝甘露", 19);
("满杯烧仙草", 21);
foreach (var VARIABLE in dictionary)
{
( + " 价格:" + );
}
输出:
芋泥波波 价格:16
黄桃果霸 价格:16
杨枝甘露 价格:19
满杯烧仙草 价格:21
可以看出IDictionary和SortedList的方法用法都大致相同,虽然它们提供的方法有些相似,但它们在实现上有所不同,比如 IDictionary 使用哈希表进行实现,而 SortedList 则使用了基于数组排序的实现。因此在某些场景下,它们的运行效率和行为可能有所不同。
这里列举一下IDictionary和SortedList方法上的差异
-
添加方法
IDictionary 提供了 Add(TKey, TValue) 方法来添加一个键值对。如果已经存在一个具有相同键的元素,则会抛出异常。
SortedList 提供了 Add(TKey, TValue) 方法来添加一个键值对。如果键已经存在,则会用新值替换旧值。
-
删除方法
IDictionary 提供了 Remove(TKey) 方法来移除一个键值对。如果不存在具有指定键的元素,则不会进行任何操作。
SortedList 提供了 Remove(TKey) 方法来移除具有指定键的键值对。
-
更新方法
IDictionary 提供了一种索引器,允许使用键指定元素来更新值。如果指定键不存在,则将添加新元素。
SortedList 提供了同样的索引器,以便根据键更新值。
-
访问方法
IDictionary 提供了 Keys 和 Values 属性,分别返回一个包含所有键和值的集合。
SortedList 提供了 Keys 和 Values 属性,分别返回一个按键或值进行排序的数组。
IDictionary和SortedList的区别
-
数据结构
IDictionary 是一种键值对集合,其中每个元素都是由一个键和一个值组成的。可以使用键来访问集合中的值。键在字典中需要是唯一的。
SortedList 基于一个排序列表实现,其中每个元素都是一个键值对。所有元素按照键进行排序,使得可以根据键进行快速的查找和访问。如同一个数组,键必须是唯一的。
-
访问
通过键访问 IDictionary 中的元素比通过索引访问 SortedList 中的元素要慢一些。因为需要使用哈希表来查找键值对。
SortedList 中的元素可以通过索引(其键在列表中的顺序)进行访问。因为元素已经按键排序,访问仅仅需要进行集合中的索引操作。
-
效率
IDictionary 的查找和修改操作通常比 SortedList 更快,因为 IDictionary 使用哈希表来快速查找(使用 Dictionary 类时)和修改。
SortedList 中的所有元素都必须按键进行排序,因此插入和删除操作通常比哈希表慢,但与数组相比较快。
-
内存使用
IDictionary 存储的数据结构通常使用哈希表,哈希表需要更多的内存用于存储哈希值和链表的指针。因此,IDictionary 需要比 SortedList 更多的内存。
SortedList 使用一个数组来存储元素,所以内存使用相对较少。
-
用途
IDictionary 适用于在集合中查找特定的值。每个键都映射到一个值,可以通过键来快速查找并获取值。它也可以被用来存储和管理任何需要一个唯一标识的对象集合。
SortedList 适用于需要按照键的顺序进行访问的集合。所有元素都按照键排序,允许按键顺序进行快速的访问。它也可用于需要实现一个简单的排序算法来排序对象集合的情况,比如一个按名称排序的集合。
ConcurrentDictionary
针对于Dictionary的线程非安全,官方提供了ConcurrentDictionary,ConcurrentDictionary是线程安全的,但如果只是进行读取操作推荐使用Dictionary,涉及到读写才使用ConcurrentDictionary ,因为ConcurrentDictionary的效率会更慢
ConcurrentDictionary<string, int> dict = new();
dict["杨枝甘露"] = 16;
//尝试添加,字段如果存在就不做处理,也不会抛异常
("杨枝甘露", 19);
(dict["杨枝甘露"]); //16
方法文档:
向字典添加新键(如果字典中尚不存在) |
TryAdd |
如果字典中当前不存在键,此方法将添加指定的键/值对。 方法返回 true 或 false ,具体取决于是否添加了新对。 |
更新字典中现有键的值(如果该键具有特定值) |
TryUpdate |
此方法检查键是否具有指定的值,如果具有指定值,则使用新值更新密钥。 它类似于 CompareExchange 方法,只不过它用于字典元素。 |
无条件地将键/值对存储在字典中,并覆盖已存在的键的值 |
索引器资源库: dictionary[key] = newValue |
|
将键/值对添加到字典,或者如果键已存在,请根据键的现有值更新键的值 |
AddOrUpdate(TKey, Func - 或 - AddOrUpdate(TKey, TValue, Func |
AddOrUpdate(TKey, Func AddOrUpdate(TKey, TValue, Func |
获取字典中某个键的值,将该值添加到字典中,如果该键不存在,则返回该值 |
GetOrAdd(TKey, TValue) - 或 - GetOrAdd(TKey, Func |
这些重载为字典中的键/值对提供延迟初始化,仅当值不存在时才添加该值。 GetOrAdd(TKey, TValue) 如果键不存在,则采用要添加的值。 GetOrAdd(TKey, Func |
所有这些操作都是原子操作,对于 类上的 ConcurrentDictionary
如下是接送委托的线程非安全方法????
//增加或修改,如果不存在,增加键值对的值为10,存在则修改为99
("满杯烧仙草", 10,(k,v)=>{return 99;});
(dict["满杯烧仙草"]); //10
("满杯烧仙草", 10,(k,v)=>{return 99;});
(dict["满杯烧仙草"]); //99
//如果存在则获取值,不存在就增加
("黄桃果霸", 17);
(dict["黄桃果霸"]); //17
("黄桃果霸", 99);
(dict["黄桃果霸"]); //17