C# 集合(Collection)(长文解析)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言
在 C# 编程中,集合(Collection) 是处理数据存储和操作的核心工具。无论是管理用户信息、商品列表,还是实现复杂的数据结构,集合都提供了灵活且高效的方式。对于初学者而言,理解集合的类型、特性和使用场景,是迈向高级编程的重要一步。本文将从基础概念出发,结合实际案例和代码示例,深入解析 C# 集合的核心知识点,帮助读者构建扎实的实践能力。
一、集合的基础概念与核心特性
1.1 什么是集合?
集合(Collection)可以理解为**“数据的容器”**,它允许开发者将多个元素(如整数、字符串、对象等)组织成一个整体,方便统一管理。例如,一个班级的学生名单可以视为一个字符串集合,而购物车中的商品列表则是一个对象集合。
集合的核心特性包括:
- 动态扩展性:大多数集合类型(如
List<T>
)支持根据需要动态增加或减少元素。 - 类型安全性:通过泛型(如
List<int>
)确保集合中只能存储指定类型的元素。 - 高效操作:不同集合类型针对特定场景优化了性能,例如快速查找、插入或删除操作。
比喻:
集合就像一个智能文件柜,每个抽屉(元素)都有明确的位置(索引或键),而柜子本身可以根据需求扩展大小,甚至支持快速检索特定抽屉的内容。
1.2 集合与数组的区别
在 C# 中,数组(Array)和集合(如 List<T>
)都是存储元素的容器,但它们存在关键差异:
特性 | 数组(Array) | 集合(如 List |
---|---|---|
大小固定性 | 创建后长度不可变 | 动态扩展,自动调整容量 |
类型安全性 | 非泛型数组(如 object[] )不安全 | 泛型集合(如 List<int> )类型安全 |
插入/删除效率 | 中间位置操作效率低 | 支持高效插入和删除(部分类型) |
内存占用 | 预分配内存,可能浪费空间 | 按需分配,更灵活 |
案例:
// 数组示例
int[] fixedArray = new int[3]; // 固定长度为3
fixedArray[0] = 10; // 直接通过索引访问
// 集合示例
List<int> dynamicList = new List<int>();
dynamicList.Add(20); // 动态添加元素
二、常用集合类型及其应用场景
2.1 List:动态数组的典范
List<T>
是 C# 中最常用的集合类型之一,它结合了数组的索引访问和动态扩展的能力。
核心方法与特性:
- Add():在末尾添加元素。
- Remove():移除指定元素的第一个匹配项。
- Contains():检查元素是否存在。
- 索引访问:通过
list[index]
获取或修改元素。
示例:学生成绩管理
List<int> scores = new List<int>();
scores.Add(85); // 添加成绩
scores.Add(92);
// 查找是否存在特定分数
if (scores.Contains(90)) {
Console.WriteLine("存在90分");
}
// 遍历输出所有成绩
foreach (int score in scores) {
Console.WriteLine(score);
}
性能注意事项:
- 插入/删除中间元素:时间复杂度为 O(n),因为需要移动后续元素。
- 容量预分配:通过
List<T>.Capacity
可避免频繁扩容。
2.2 Dictionary<TKey, TValue>:键值对的高效管理
Dictionary<TKey, TValue>
用于存储键值对(Key-Value),通过唯一键快速检索值。
核心特性:
- 唯一键约束:每个键必须唯一,否则会抛出异常。
- 快速查找:通过键访问值的时间复杂度为 O(1)。
- 支持泛型:键和值均可指定类型(如
Dictionary<string, int>
)。
示例:学生成绩字典
Dictionary<string, int> studentScores = new Dictionary<string, int>();
studentScores.Add("Alice", 85);
studentScores.Add("Bob", 92);
// 通过键获取值
int aliceScore = studentScores["Alice"]; // 输出85
// 安全访问(避免KeyNotFoundException)
if (studentScores.TryGetValue("Charlie", out int charlieScore)) {
Console.WriteLine(charlieScore);
} else {
Console.WriteLine("未找到Charlie");
}
2.3 HashSet:无序且唯一的集合
HashSet<T>
用于存储不重复的元素,适用于需要快速判断元素存在性或去重的场景。
核心方法:
- Add():添加元素,若已存在则返回 false。
- Contains():检查元素是否存在。
- UnionWith():合并两个集合。
示例:去重用户输入
HashSet<string> uniqueWords = new HashSet<string>();
string input = "apple,banana,apple,orange";
foreach (string word in input.Split(',')) {
uniqueWords.Add(word.Trim()); // 自动去重
}
// 输出去重后的结果
foreach (var word in uniqueWords) {
Console.WriteLine(word); // 仅输出apple、banana、orange
}
2.4 其他常用集合类型
集合类型 | 适用场景 |
---|---|
Queue<T> | 先进先出(FIFO)的队列,如任务调度 |
Stack<T> | 后进先出(LIFO)的栈,如递归模拟 |
LinkedList<T> | 频繁插入/删除中间元素的场景 |
SortedSet<T> | 自动排序且唯一元素的集合 |
三、集合的高级操作与性能优化
3.1 枚举器与遍历
集合通常通过枚举器(Enumerator) 实现遍历。开发者可以通过 foreach
循环或手动获取 GetEnumerator()
方法直接操作枚举器。
注意事项:
- 在遍历过程中不可修改集合(如添加或删除元素),否则会引发
InvalidOperationException
。 - 若需在遍历时修改集合,可使用
ToList()
或ToArray()
创建副本。
// 安全遍历示例
foreach (var item in myCollection.ToList()) {
if (item < 50) {
myCollection.Remove(item); // 直接操作原始集合
}
}
3.2 集合的排序与转换
通过 List<T>.Sort()
方法或 LINQ 查询,可以对集合进行排序或转换。
示例:按成绩降序排序
List<int> scores = new List<int> { 85, 92, 78, 95 };
scores.Sort(); // 默认升序排序
// 使用 LINQ 实现降序排序
var sortedScores = scores.OrderByDescending(s => s).ToList();
3.3 集合的线程安全
在多线程环境中,需使用线程安全的集合(如 ConcurrentQueue<T>
)或手动加锁。
示例:线程安全的计数器
// 非线程安全的错误示例
int count = 0;
Parallel.For(0, 1000, _ => count++); // 可能导致数据不一致
// 正确方式:使用线程安全的集合
ConcurrentBag<int> concurrentCount = new ConcurrentBag<int>();
Parallel.For(0, 1000, _ => concurrentCount.Add(1));
四、最佳实践与常见误区
4.1 如何选择合适的集合类型?
- 需要快速查找或更新键值对 →
Dictionary<TKey, TValue>
- 需要动态数组且频繁访问索引 →
List<T>
- 需要唯一元素且无需排序 →
HashSet<T>
- 需要频繁插入/删除中间元素 →
LinkedList<T>
4.2 性能优化技巧
- 预分配容量:对于
List<T>
,通过list.Capacity = estimatedSize
减少扩容开销。 - 避免频繁的集合操作:在循环中,尽量减少
Contains()
调用,改用HashSet<T>
提升效率。
4.3 常见误区
- 错误 1:将非泛型集合(如
ArrayList
)与泛型集合混用,导致类型转换错误。 - 错误 2:在遍历时直接修改集合,引发异常。
结论
C# 集合(Collection)是开发者高效处理数据的核心工具。通过理解不同集合类型的特性(如 List<T>
的动态扩展、Dictionary
的键值对快速查找、HashSet
的唯一性约束),开发者可以针对具体场景选择最优方案。本文通过代码示例和性能分析,帮助读者掌握从基础到高级的集合用法,同时强调线程安全、内存管理和性能优化等关键点。掌握这些知识,将显著提升代码的健壮性和执行效率,为构建复杂应用程序奠定坚实基础。
如需进一步深入,可探索 LINQ
查询语法、集合的序列化,以及异步集合的高级用法。编程之路永无止境,但掌握集合的精髓,无疑是迈向专业开发的重要一步。