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

C#集合:从基础到进阶的全面解析

C#集合:从基础到进阶的全面解析

在 C# 编程中,集合是处理数据集合的核心工具。无论是存储一组对象、实现缓存机制,还是处理复杂的数据结构,都离不开集合的灵活运用。本文将全面深入地探讨 C# 集合体系,从基础概念到高级技巧,帮助开发者掌握集合的精髓,写出更高效、更优雅的代码。

一、集合概述与分类

C# 集合框架是.NET类库的重要组成部分,它提供了一系列用于存储和操作数据的类和接口。与数组相比,集合具有动态扩容、类型安全(泛型集合)、丰富操作方法等优势,是处理可变数据量场景的首选。

1. 泛型与非泛型集合

C# 集合分为泛型集合和非泛型集合两大类:

  • 非泛型集合:位于System.Collections命名空间,如ArrayListHashtable等。它们存储object类型,存在装箱拆箱操作,性能较差,且缺乏编译时类型检查,现在已逐渐被泛型集合取代。
  • 泛型集合:位于System.Collections.Generic命名空间,如List<T>Dictionary<TKey, TValue>等。通过泛型参数指定元素类型,避免了装箱拆箱,提供类型安全,是现代 C# 开发的主流选择。

2. 集合接口体系

集合框架基于接口构建,核心接口包括:

  • IEnumerable<T>:支持泛型迭代,定义了GetEnumerator()方法,是所有可迭代集合的基础。
  • ICollection<T>:继承自IEnumerable<T>,增加了元素数量、添加、删除等操作。
  • IList<T>:继承自ICollection<T>,提供索引访问能力,如List<T>实现了该接口。
  • IDictionary<TKey, TValue>:定义键值对集合的操作,如Dictionary<TKey, TValue>

二、常用集合类型详解

1. List:动态数组的首选

List<T>是最常用的集合类型,内部通过数组实现,支持动态扩容。

// 初始化与基本操作
var fruits = new List<string>();
fruits.Add("Apple");
fruits.AddRange(new[] { "Banana", "Cherry" });
fruits.Insert(1, "Orange");// 便利
foreach (var fruit in fruits)
{Console.WriteLine(fruit);
}// 查找与过滤
var result = fruits.Find(f => f.StartsWith("A"));
var filtered = fruits.FindAll(f => f.Length > 5);

内部原理List<T>初始容量为 4,当元素数量超过容量时,会创建一个新的、容量为原来 2 倍的数组,并复制旧元素,因此频繁扩容会影响性能。建议初始化时指定初始容量(如new List<string>(100)),减少扩容次数。

2. Dictionary<TKey, TValue>:键值对高效映射

Dictionary<TKey, TValue>基于哈希表实现,提供 O (1) 的平均查找效率,适用于通过键快速访问值的场景。

var studentScores = new Dictionary<int, int>
{{ 101, 90 },{ 102, 85 }
};// 添加与访问
studentScores.Add(103, 95);if (studentScores.TryGetValue(102, out int score))
{Console.WriteLine($"Score: {score}");
}// 遍历键值对
foreach (var kvp in studentScores)
{Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
}

哈希冲突处理:当两个键的哈希码相同时,Dictionary通过链表将冲突的元素存储在同一桶中,此时查找复杂度退化为 O (n)。因此,应确保键类型实现了良好的GetHashCodeEquals方法。

3. HashSet:高性能去重集合

HashSet<T>是无序集合,不允许重复元素,基于哈希表实现,添加、删除、包含操作的平均时间复杂度为 O (1)。

var uniqueNumbers = new HashSet<int> { 1, 2, 3 };
uniqueNumbers.Add(2); // 重复元素,添加失败
uniqueNumbers.Remove(3);bool contains = uniqueNumbers.Contains(1); // 高效判断包含

适用场景:去重、集合运算(如交集IntersectWith、并集UnionWith)。

4. Queue与 Stack:特殊访问模式集合

  • Queue:先进先出(FIFO)集合,适用于消息队列、任务调度等场景。

    var queue = new Queue<string>();
    queue.Enqueue("First");
    queue.Enqueue("Second");var item = queue.Dequeue(); // 取出"First"
    
  • Stack:后进先出(LIFO)集合,适用于表达式求值、撤销操作等。

    var stack = new Stack<int>();
    stack.Push(1);
    stack.Push(2);var top = stack.Pop(); // 取出2
    

5. LinkedList:链表结构的灵活实现

LinkedList<T>是双向链表,元素通过节点链接,插入和删除元素(已知节点位置时)效率高(O (1)),但随机访问效率低(O (n))。

var linkedList = new LinkedList<string>();
var node = linkedList.AddFirst("First");
linkedList.AddAfter(node, "Second");

适用场景:需要频繁在集合中间插入 / 删除元素的场景。

三、性能分析与选择策略

不同集合类型的性能特性差异显著,选择合适的集合是优化代码的关键。

集合类型查找效率插入 / 删除效率(中间位置)适用场景
List<T>O(1)O(n)动态数组、随机访问频繁
Dictionary<,>O(1)O (1)(平均)键值对映射、快速查找
HashSet<T>O(1)O (1)(平均)去重、集合运算
LinkedList<T>O(n)O (1)(已知节点)频繁插入 / 删除中间元素
Queue<T>O(n)O (1)(队尾)FIFO 场景,如消息队列
Stack<T>O(n)O (1)(栈顶)LIFO 场景,如撤销操作

选择策略

  1. 根据核心操作(查找、插入、删除)的频率选择。
  2. 考虑数据是否有序、是否允许重复。
  3. 预估数据量,初始化时指定合适容量(如List<T>Dictionary<,>)。

四、线程安全与并发集合

多线程环境下,普通集合(如List<T>Dictionary<,>)不是线程安全的,并发操作可能导致数据异常。.NET 提供了专门的并发集合(System.Collections.Concurrent命名空间):

  • ConcurrentBag<T>:无序的线程安全集合,适合多线程添加和获取元素。
  • ConcurrentDictionary<TKey, TValue>:线程安全的键值对集合。
  • ConcurrentQueue<T>/ConcurrentStack<T>:线程安全的队列和栈。
// 并发字典示例
var concurrentDict = new ConcurrentDictionary<int, string>();
concurrentDict.TryAdd(1, "One");
if (concurrentDict.TryUpdate(1, "NewOne", "One"))
{// 更新成功
}

使用建议:多线程读写时优先使用并发集合,避免手动加锁的复杂性。

五、最佳实践与高级技巧

1. 优先使用泛型集合

非泛型集合(如ArrayList)会导致装箱拆箱操作,影响性能且缺乏类型安全。例如:

// 不推荐:ArrayList存在装箱拆箱
ArrayList list = new ArrayList();
list.Add(1); // 装箱
int value = (int)list[0]; // 拆箱// 推荐:List<T>类型安全且无装箱
List<int> intList = new List<int>();
intList.Add(1);
int val = intList[0];

2. 利用接口编程

依赖抽象接口(如IEnumerable<T>ICollection<T>)而非具体实现,提高代码灵活性。例如,方法参数使用IEnumerable<T>,可接受任何实现该接口的集合:

public void ProcessItems(IEnumerable<string> items)
{foreach (var item in items){// 处理逻辑}
}

3. 集合初始化与 LINQ 结合

C# 支持集合初始化器和 LINQ 查询,简化集合操作:

// 集合初始化器
var numbers = new List<int> { 1, 2, 3, 4, 5 };// LINQ查询与集合转换
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();var numberDict = numbers.ToDictionary(n => n, n => n * 2);

4. 自定义集合

当系统集合无法满足需求时,可通过实现IEnumerable<T>ICollection<T>等接口创建自定义集合:

public class ReadOnlyList<T> : IEnumerable<T>
{private readonly List<T> _innerList;public ReadOnlyList(List<T> list){_innerList = new List<T>(list);}public IEnumerator<T> GetEnumerator(){return _innerList.GetEnumerator();}IEnumerator IEnumerable.GetEnumerator(){return GetEnumerator();}
}

六、总结

C# 集合框架提供了丰富的类型和功能,从基础的List<T>到复杂的并发集合,每种类型都有其独特的适用场景。掌握集合的内部原理、性能特性和最佳实践,能帮助开发者编写更高效、更健壮的代码。在实际开发中,应根据具体需求(如操作类型、数据量、线程环境)选择合适的集合类型,并充分利用泛型和 LINQ 提升开发效率。通过不断实践和深入理解,才能真正发挥 C# 集合的强大威力。

http://www.dtcms.com/a/272825.html

相关文章:

  • 力扣-48.旋转图像
  • 文件追加模式:编写一个程序,向一个已存在的文件末尾追加内容。
  • ADVANTEST R4131 SPECTRUM ANALYZER 光谱分析仪
  • 有缺陷的访问控制
  • Agent调用(高德地图)MCP服务
  • Java虚拟机栈Test01
  • 盲盒一番赏小程序技术实现方案:高并发与防作弊的平衡之道
  • C#System.Runtime.InteropServices.ExternalException (0x80004005): GDI+ 中发生一般性错误。
  • Kettle导入Excel文件进数据库时,数值发生错误的一种原因
  • 计算机视觉速成 之 概述
  • Ubuntu如何快速搭建docker以及使用代理访问
  • Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
  • 数据结构 顺序表(1)
  • 等保-linux-三权分立账号设置,系统管理员、安全管理员、审计管理员
  • 目标检测中的评价指标计算
  • 数据结构 —— 键值对 map
  • Git操作技巧(一)
  • shell学习从入门到精通
  • 数据仓库:企业数据管理的核心枢纽
  • 创客匠人视角:从信任经济看创始人 IP 如何重塑 IP 变现逻辑
  • nmap扫描指定网段中指定开放端口的主机(IP,mac地址)
  • 量子计算系统软件:让“脆弱”的量子计算机真正可用
  • 有什么好使用的内网ip映射软件?可以让家庭/公司网络局域网端口,让公网互联网跨网访问
  • Spring boot整合dubbo+zookeeper
  • 26-计组-外存
  • 基于云端EC2的O3DE机器人仿真环境搭建指南
  • python自动化面试问题——关于python
  • 029_构造器重载与默认构造器
  • 【报错】Could not find a package configuration file provided by ‘glog’”
  • “Datawhale AI夏令营”基于带货视频评论的用户洞察挑战赛