目录
三、关于TryGetValue(TKey key, out TValue value) 方法的详细说明
关于默认的排序可以看这篇文章的第二点中关于排序的部分:
一、SortedDictionary<TKey, TValue>
1. 核心特性
-
底层实现:基于 红黑树(自平衡二叉搜索树),确保元素始终按键排序。
-
排序规则:默认按键的自然顺序(
IComparable<TKey>
)排序,或通过自定义IComparer<TKey>
指定。 -
性能特点:
插入/删除:时间复杂度为
O(log n)
(高效动态调整树结构)。查找:时间复杂度为
O(log n)
。内存占用:较高(每个节点需要存储左右子树指针)。
-
适用场景:
需要频繁插入/删除键值对,同时保持有序。
需要按键顺序遍历或范围查询(如获取某个区间内的键)。
2. 常用方法和属性
方法/属性 | 说明 |
---|---|
Add(TKey key, TValue value) | 添加键值对(若键已存在,抛出 ArgumentException )。 |
Remove(TKey key) | 删除指定键的键值对(返回是否成功)。 |
ContainsKey(TKey key) | 检查是否包含指定键。 |
TryGetValue(TKey key, out TValue value) | 安全获取键对应的值(返回是否成功)。 |
Keys | 获取按键排序的键集合(KeyCollection 类型)。 |
Values | 获取按键顺序排列的值集合(ValueCollection 类型)。 |
Count | 获取键值对的数量。 |
使用示例:
using System;
using System.Collections.Generic;
// 自定义比较器:按字符串长度排序
public class StringLengthComparer : IComparer<string>
{
public int Compare(string x, string y)
{
int lengthCompare = x.Length.CompareTo(y.Length);
// 长度相同则按字典序排序
return lengthCompare != 0 ? lengthCompare : string.Compare(x, y, StringComparison.Ordinal);
}
}
public class SortedDictionaryExample
{
public static void Main()
{
// 使用自定义比较器初始化
var dict = new SortedDictionary<string, int>(new StringLengthComparer());
// 添加元素
dict.Add("Banana", 3);
dict.Add("Apple", 5);
dict.Add("Cherry", 2);
dict.Add("Kiwi", 7);
// 删除元素
bool removed = dict.Remove("Apple");
Console.WriteLine($"删除 Apple: {removed}"); // True
// 修改元素
dict["Kiwi"] = 10;
// 遍历输出(按键长度升序,长度相同按字典序)
Console.WriteLine("SortedDictionary 元素:");
foreach (var kvp in dict)
{
Console.WriteLine($"{kvp.Key} (长度 {kvp.Key.Length}): {kvp.Value}");
}
// 输出顺序:
// Kiwi (长度 4): 10
// Apple (长度 5): 5
// Banana (长度 6): 3
// Cherry (长度 6): 2
}
}
结果:
二、SortedList<TKey, TValue>
1. 核心特性
-
底层实现:基于 两个动态数组,分别存储键和值,按键排序。
-
排序规则:与
SortedDictionary
相同,默认按键的自然顺序或自定义IComparer<TKey>
。 -
性能特点:
插入/删除:时间复杂度为
O(n)
(需移动元素以保持数组有序)。查找:时间复杂度为
O(log n)
(二分查找)。内存占用:较低(数组连续存储,无额外指针开销)。
-
适用场景:
键值对数量较少,且插入/删除操作不频繁。
需要按键快速查找,且内存敏感的场景。
需要通过索引(类似列表)访问键或值。
2. 常用方法和属性
方法/属性 | 说明 |
---|---|
Add(TKey key, TValue value) | 添加键值对(若键已存在,抛出 ArgumentException )。 |
Remove(TKey key) | 删除指定键的键值对(返回是否成功)。 |
ContainsKey(TKey key) | 检查是否包含指定键。 |
TryGetValue(TKey key, out TValue value) | 安全获取键对应的值(返回是否成功)。 |
IndexOfKey(TKey key) | 返回键的索引(基于排序后的顺序)。 |
IndexOfValue(TValue value) | 返回值的索引(线性搜索,效率较低)。 |
Keys | 获取按键排序的键集合(IList<TKey> 类型)。 |
Values | 获取按键顺序排列的值集合(IList<TValue> 类型)。 |
Count | 获取键值对的数量。 |
使用示例:
using System;
using System.Collections.Generic;
// 自定义比较器:整型降序排序
public class ReverseIntComparer : IComparer<int>
{
public int Compare(int x, int y)
{
return y.CompareTo(x); // 反转默认顺序
}
}
public class SortedListExample
{
public static void Main()
{
// 使用自定义比较器初始化
var list = new SortedList<int, string>(new ReverseIntComparer());
// 添加元素
list.Add(3, "Three");
list.Add(1, "One");
list.Add(4, "Four");
list.Add(2, "Two");
// 删除元素
bool removed = list.Remove(1);
Console.WriteLine($"删除 1: {removed}"); // True
// 修改元素
list[4] = "Four (Updated)";
// 遍历输出(按键降序)
Console.WriteLine("SortedList 元素:");
foreach (var kvp in list)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
// 输出顺序:
// 4: Four (Updated)
// 3: Three
// 2: Two
// 通过索引访问键和值
Console.WriteLine("第一个键: " + list.Keys[0]); // 4
Console.WriteLine("最后一个值: " + list.Values[list.Count - 1]); // Two
}
}
三、关于TryGetValue(TKey key, out TValue value)
方法的详细说明
(一)作用
TryGetValue
是字典类数据结构(如Dictionary<TKey, TValue>
、SortedDictionary<TKey, TValue>
、SortedList<TKey, TValue>
)中的一个核心方法,用于安全地获取与指定键关联的值。它的核心优势是避免因键不存在而抛出异常,同时通过一次查找操作完成“检查存在性”和“获取值”两个步骤。
(二)语法和参数
bool TryGetValue(TKey key, out TValue value)
输入参数:
key
:要查找的键。输出参数:
value
:当键存在时,此参数会被赋值为对应的值;当键不存在时,会被设置为TValue
类型的默认值(如int
的默认值是0
,引用类型是null
)。返回值:
true
:键存在,且value
被正确赋值。
false
:键不存在,value
被设为默认值。
注意一定要加这个out!!!
在SortedDictioary中的示例:在SortedList中也一样,这里随便拿个示例。
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var dict = new SortedDictionary<string, int>
{
{ "Apple", 10 },
{ "Banana", 20 },
{ "Cherry", 30 }
};
// 尝试获取存在的键
if (dict.TryGetValue("Banana", out int bananaValue))
{
Console.WriteLine($"Banana 的值是: {bananaValue}"); // 输出: 20
}
// 尝试获取不存在的键
if (!dict.TryGetValue("Mango", out int mangoValue))
{
Console.WriteLine("Mango 不存在,mangoValue 被设为默认值: " + mangoValue); // 输出: 0
}
}
}
(三)注意事项
out
参数无需初始化:
// 不需要提前初始化 value
if (dict.TryGetValue("key", out string value))
{
// 仅在返回 true 时,value 是有效的
}
默认值的陷阱:
-
如果键不存在,
value
会被设为default(TValue)
。对于引用类型是null
,值类型是0
、false
等。 -
必须检查返回值后再使用
value
,否则可能因默认值导致逻辑错误。
与索引器的对比:
方式 | 键存在 | 键不存在 |
---|---|---|
dict[key] | 返回值 | 抛出 KeyNotFoundException |
TryGetValue | 返回 true | 返回 false ,value 为默认值 |
四、总结
在C#的泛型集合中,SortedDictionary<TKey, TValue>
和SortedList<TKey, TValue>
均提供了按键自动排序的功能,但两者在实现机制、性能特性和适用场景上有显著差异,我们需根据实际需求选择合适的数据结构。
SortedDictionary的核心特性:
基于二叉搜索树(通常为红黑树)实现,插入和删除操作的时间复杂度为O(log n)
,适合频繁增删元素的场景。其内存布局非连续,因此在遍历时效率略低于数组结构。它不直接支持通过索引访问键值对,但提供按键排序的Keys
和Values
集合。自定义排序规则需通过IComparer<TKey>
实现,例如按字符串长度排序时,可在比较器中先比较长度再按字典序处理。
SortedList的核心特性:
基于动态数组实现,通过二分查找维护有序性。插入和删除操作的平均时间复杂度为O(n)
(需移动元素),适合数据变动较少的场景。其优势在于内存连续,遍历和按索引访问(如IndexOfKey
)效率更高。此外,SortedList
允许通过索引直接访问键值对(如Keys[0]
),但IndexOfValue
需线性搜索,效率较低。自定义比较器同样支持反向排序(如整型降序)。
TryGetValue方法的注意事项:
两者均提供TryGetValue(TKey key, out TValue value)
方法,用于安全获取值。此方法通过out
参数返回结果,调用前无需初始化value
。若键存在,返回true
且value
有效;若键不存在,返回false
且value
为类型默认值(如数值类型为0,引用类型为null
)。我们需注意默认值陷阱,例如当值为0时,需结合返回值区分“键不存在”和“值本身为0”的情况。与索引器dict[key]
相比,TryGetValue
避免了KeyNotFoundException
异常,更适合不确定键是否存在的场景。
选择策略:
- 优先选择SortedDictionary的场景:数据频繁插入/删除、数据量较大、无需索引访问。
- 优先选择SortedList的场景:数据初始化后变动较少、需要按索引快速访问、内存使用需紧凑。
示例场景对比:
- 实时高频更新的缓存(如股票价格)适合用
SortedDictionary
; - 配置项(启动时加载,后续只读)适合用
SortedList
以快速按索引访问。
综上,理解两者的底层实现及性能特点,结合业务需求选择合适结构,是优化程序效率和资源消耗的关键。同时,合理使用TryGetValue
替代索引器访问,可提升代码健壮性。