当前位置: 首页 > news >正文

C#进阶学习(八)常见的泛型数据结构类(3)SortedDictionary<TKey, TValue>与SortedList<TKey, TValue>

       

目录

       

        关于默认的排序可以看这篇文章的第二点中关于排序的部分:

一、SortedDictionary

1. 核心特性

2. 常用方法和属性

二、SortedList

1. 核心特性

2. 常用方法和属性

三、关于TryGetValue(TKey key, out TValue value) 方法的详细说明

(一)作用

(二)语法和参数 

注意一定要加这个out!!! 

(三)注意事项

四、总结


        关于默认的排序可以看这篇文章的第二点中关于排序的部分:

常见的泛型数据结构(2)

一、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]); // 4Console.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,值类型是 0false 等。

  • 必须检查返回值后再使用 value,否则可能因默认值导致逻辑错误。

与索引器的对比

方式键存在键不存在
dict[key]返回值抛出 KeyNotFoundException
TryGetValue返回 true返回 falsevalue 为默认值

四、总结

        在C#的泛型集合中,SortedDictionary<TKey, TValue>SortedList<TKey, TValue>均提供了按键自动排序的功能,但两者在实现机制、性能特性和适用场景上有显著差异,我们需根据实际需求选择合适的数据结构。

SortedDictionary的核心特性:​
        基于二叉搜索树(通常为红黑树)实现,插入和删除操作的时间复杂度为O(log n),适合频繁增删元素的场景。其内存布局非连续,因此在遍历时效率略低于数组结构。它不直接支持通过索引访问键值对,但提供按键排序的KeysValues集合。自定义排序规则需通过IComparer<TKey>实现,例如按字符串长度排序时,可在比较器中先比较长度再按字典序处理。

SortedList的核心特性:​
        基于动态数组实现,通过二分查找维护有序性。插入删除操作平均时间复杂度为O(n)(需移动元素),适合数据变动较少的场景。其优势在于内存连续,遍历和按索引访问(IndexOfKey)效率更高。此外,SortedList允许通过索引直接访问键值对(如Keys[0]),但IndexOfValue需线性搜索,效率较低。自定义比较器同样支持反向排序(如整型降序)。

TryGetValue方法的注意事项:​
        两者均提供TryGetValue(TKey key, out TValue value)方法,用于安全获取值。此方法通过out参数返回结果,调用前无需初始化value。若键存在,返回truevalue有效;若键不存在,返回falsevalue为类型默认值(如数值类型为0,引用类型为null)。我们需注意默认值陷阱,例如当值为0时,需结合返回值区分“键不存在”和“值本身为0”的情况。与索引器dict[key]相比,TryGetValue避免了KeyNotFoundException异常,更适合不确定键是否存在的场景。

选择策略:​

  • 优先选择SortedDictionary的场景​:数据频繁插入/删除、数据量较大、无需索引访问。
  • 优先选择SortedList的场景​:数据初始化后变动较少、需要按索引快速访问、内存使用需紧凑。

示例场景对比:​

  • 实时高频更新的缓存(如股票价格)适合用SortedDictionary
  • 配置项(启动时加载,后续只读)适合用SortedList以快速按索引访问。

        综上,理解两者的底层实现及性能特点,结合业务需求选择合适结构,是优化程序效率和资源消耗的关键。同时,合理使用TryGetValue替代索引器访问,可提升代码健壮性。

相关文章:

  • OJ - 设计循环队列
  • 交换机端口安全
  • C++学习:六个月从基础到就业——内存管理:内存泄漏与避免
  • chili3d调试6 添加左侧面板
  • 【第四十一周】文献阅读:HippoRAG:受神经生物学启发的大型语言模型长期记忆机制
  • OSPF特殊区域
  • 金融图QCPFinancial
  • mac监控linux上mysql性能(Grafana+Prometheus+mysqld_exporter)
  • VSCode PIO使用Jlink SWD烧录Stm32
  • 【C++初阶】第15课—模版进阶
  • 进程与线程:01 CPU管理的直观想法
  • 股票分析技术指标【RSV、KDJ】
  • 【Ollama:本地LLM工具】
  • windows服务器及网络:论如何安装(虚拟机)
  • 驱动开发硬核特训 · Day 15:电源管理核心知识与实战解析
  • 基于javaweb的SpringBoot儿童爱心管理系统设计与实现(源码+文档+部署讲解)
  • go语言对http协议的支持
  • 神经网络与模型训练过程笔记
  • PyTorch 深度学习实战(39):归一化技术对比(BN/LN/IN/GN)
  • 提示词设计:动态提示词 标准提示词
  • 抗美援朝老战士、华西医院精神科学术带头人之一袁德基逝世
  • 电动自行车新国标将于9月1日落地,首批6家检测机构出炉
  • 著名政治学学者、中国人民大学教授仝志敏逝世
  • 智慧菜场团标试验:标准化的同时还能保留个性化吗?
  • 澳门世界杯“中日对决”,蒯曼击败伊藤美诚晋级女单决赛
  • 成了“一日顶流”又能如何?