跳到主要内容

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();

小结

  1. List<T>:动态数组,支持索引访问
  2. Dictionary<TKey, TValue>:键值对映射
  3. HashSet<T>:不重复集合
  4. Queue<T>:先进先出队列
  5. Stack<T>:后进先出栈
  6. LinkedList<T>:双向链表

练习

  1. 统计字符串中每个字符出现的次数(使用 Dictionary)
  2. 实现一个简单的任务队列(使用 Queue)
  3. 实现去重功能(使用 HashSet)
  4. 实现分页查询功能