C# 集合
本章将介绍 C# 中的集合类型,包括泛型集合、非泛型集合、以及相关操作。
集合概述
集合分类
| 类型 | 说明 |
|---|---|
| List<T> | 动态数组 |
| Dictionary<TKey, TValue> | 键值对 |
| HashSet<T> | 无序集合(不重复) |
| Queue<T> | 队列(先进先出) |
| Stack<T> | 栈(后进先出) |
| LinkedList<T> | 双向链表 |
| SortedList<T> | 排序列表 |
List<T>
创建和初始化
// 创建空列表
List<int> list = new List<int>();
// 创建时指定容量
List<string> names = new List<string>(100);
// 初始化
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// 从数组创建
int[] array = { 1, 2, 3 };
List<int> fromArray = new List<int>(array);
基本操作
List<int> list = new List<int> { 1, 2, 3 };
// 添加元素
list.Add(4); // 末尾添加
list.AddRange(new[] { 5, 6 }); // 批量添加
list.Insert(0, 0); // 指定位置插入
// 删除元素
list.Remove(3); // 删除第一个匹配的
list.RemoveAt(0); // 删除指定索引
list.RemoveRange(0, 2); // 删除范围
list.Clear(); // 清空所有
// 访问元素
int first = list[0];
int last = list[list.Count - 1];
// 修改
list[0] = 100;
// 查找
int index = list.IndexOf(3); // 查找索引
bool exists = list.Contains(5); // 检查存在
常用方法
List<int> list = new List<int> { 5, 2, 8, 1, 9 };
// 排序
list.Sort(); // 升序排序
list.Sort((a, b) => b - a); // 降序
// 反转
list.Reverse();
// 查找
int max = list.Max();
int min = list.Min();
int sum = list.Sum();
// 转换
int[] array = list.ToArray();
List<T> 的内部原理
// List\<T> 实际上是动态数组
// 当容量不足时,会创建更大的数组并复制元素
// 建议:在已知大小时指定容量,避免频繁扩容
List<int> list = new List<int>(10000); // 预分配容量
for (int i = 0; i < 10000; i++)
{
list.Add(i); // 不会频繁扩容
}
Dictionary<TKey, TValue>
创建和使用
// 创建字典
Dictionary<string, int> scores = new Dictionary<string, int>();
// 添加元素
scores["张三"] = 95;
scores["李四"] = 87;
scores.Add("王五", 92);
// 访问
int zhangSan = scores["张三"]; // 如果不存在会抛异常
// 安全访问
if (scores.TryGetValue("张三", out int score))
{
Console.WriteLine($"张三: {score}");
}
遍历字典
Dictionary<string, int> scores = new Dictionary<string, int>
{
{ "张三", 95 },
{ "李四", 87 },
{ "王五", 92 }
};
// 遍历键
foreach (var name in scores.Keys)
{
Console.WriteLine(name);
}
// 遍历值
foreach (var score in scores.Values)
{
Console.WriteLine(score);
}
// 遍历键值对
foreach (var kvp in scores)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
// 使用 LINQ
var topStudents = scores
.Where(kvp => kvp.Value >= 90)
.Select(kvp => kvp.Key);
常见操作
Dictionary<string, int> dict = new Dictionary<string, int>();
// 删除
dict.Remove("张三"); // 删除指定键
// 检查存在
bool has = dict.ContainsKey("李四");
bool hasValue = dict.ContainsValue(90);
// 获取或添加
int value = dict.GetValueOrDefault("赵六", 0); // 不存在返回默认值
dict.TryAdd("赵六", 85); // 添加成功返回 true,已存在返回 false
// 使用 LINQ 进行分组统计
var groups = scores.GroupBy(kvp => kvp.Value / 10 * 10);
HashSet<T>
特点和应用
// HashSet:不允许重复的无序集合
HashSet<int> uniqueNumbers = new HashSet<int>();
uniqueNumbers.Add(1);
uniqueNumbers.Add(2);
uniqueNumbers.Add(1); // 不会添加,忽略
Console.WriteLine(uniqueNumbers.Count); // 2
集合操作
HashSet<int> set1 = new HashSet<int> { 1, 2, 3, 4, 5 };
HashSet<int> set2 = new HashSet<int> { 4, 5, 6, 7, 8 };
// 并集
set1.UnionWith(set2); // {1,2,3,4,5,6,7,8}
// 交集
set1.IntersectWith(set2); // {4,5}
// 差集
set1.ExceptWith(set2); // {1,2,3}
// 对称差集
set1.SymmetricExceptWith(set2); // {1,2,3,6,7,8}
// 检查子集
bool isSubset = set1.IsSubsetOf(new[] { 1, 2, 3, 4, 5, 6, 7, 8 }); // true
Queue<T> 和 Stack<T>
Queue(队列)
// 队列:先进先出
Queue<string> queue = new Queue<string>();
queue.Enqueue("第一个");
queue.Enqueue("第二个");
queue.Enqueue("第三个");
// 查看但不移除
string peek = queue.Peek();
// 取出
string first = queue.Dequeue(); // "第一个"
string second = queue.Dequeue(); // "第二个"
Stack(栈)
// 栈:后进先出
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
// 查看栈顶
int top = stack.Peek(); // 3
// 弹出
int pop = stack.Pop(); // 3
pop = stack.Pop(); // 2
LinkedList<T>
双向链表
LinkedList<int> list = new LinkedList<int>();
// 添加节点
list.AddFirst(1); // 头部添加
list.AddLast(5); // 尾部添加
list.AddAfter(list.First, 2); // 在第一个节点后添加
list.AddBefore(list.Last, 4); // 在最后一个节点前添加
// 遍历
foreach (int item in list)
{
Console.WriteLine(item);
}
// 访问节点
var first = list.First;
var last = list.Last;
// 删除
list.RemoveFirst();
list.RemoveLast();
list.Remove(2);
集合的转换
相互转换
// 数组转 List
int[] array = { 1, 2, 3 };
List<int> list = array.ToList();
// List 转数组
int[] arr = list.ToArray();
// List 转 Dictionary
List<Person> people = new List<Person>
{
new Person { Id = 1, Name = "张三" },
new Person { Id = 2, Name = "李四" }
};
Dictionary<int, string> dict = people.ToDictionary(p => p.Id, p => p.Name);
// 数组转 HashSet
HashSet<int> set = array.ToHashSet();
// Dictionary 转 List
var items = dict.Select(kvp => new { Key = kvp.Key, Value = kvp.Value }).ToList();
泛型约束
where 关键字
// T 必须是值类型
void Method1<T>() where T : struct { }
// T 必须是引用类型
void Method2<T>() where T : class { }
// T 必须有无参构造函数
void Method3<T>() where T : new() { }
// T 必须继承自某个类
void Method4<T>() where T : BaseClass { }
// T 必须实现某个接口
void Method5<T>() where T : IDisposable { }
常用集合操作
使用 LINQ 操作集合
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 过滤
var evens = numbers.Where(n => n % 2 == 0);
// 转换
var squares = numbers.Select(n => n * n);
// 分组
var groups = numbers.GroupBy(n => n % 2 == 0 ? "偶数" : "奇数");
// 排序
var sorted = numbers.OrderByDescending(n => n).ToList();
// 分页
var page = numbers.Skip(5).Take(5);
// 聚合
var sum = numbers.Sum();
var avg = numbers.Average();
小结
- List<T>:动态数组,支持索引访问
- Dictionary<TKey, TValue>:键值对映射
- HashSet<T>:不重复集合
- Queue<T>:先进先出队列
- Stack<T>:后进先出栈
- LinkedList<T>:双向链表
练习
- 统计字符串中每个字符出现的次数(使用 Dictionary)
- 实现一个简单的任务队列(使用 Queue)
- 实现去重功能(使用 HashSet)
- 实现分页查询功能