C# 集合(Collection)
`List`、`Dictionary`、`HashSet`、`Queue`、`Stack`、以及 LINQ。
Key Points按访问模式选集合:按索引用 `List`,按键用 `Dictionary`,去重用 `HashSet`。
选哪个
| Collection | Lookup | Order | Duplicates |
|---|---|---|---|
List<T> | 索引 O(1),值 O(n) | 插入顺序 | 是 |
Dictionary<K,V> | 键 O(1) | 无序 | 键唯一 |
HashSet<T> | 值 O(1) | 无序 | 否 |
SortedDictionary | 键 O(log n) | 按键排序 | 键唯一 |
Queue<T> | 先进先出 | FIFO | 是 |
Stack<T> | 后进先出 | LIFO | 是 |
LinkedList<T> | 插入删除 O(1) | 链表顺序 | 是 |
List
C#
var list = new List<int> { 3, 1, 4 };
list.Add(5);
list.AddRange(new[] { 9, 2 });
list.Insert(0, 0);
list.Remove(4);
list.RemoveAt(0);
Console.WriteLine(string.Join(",", list));
Console.WriteLine(list.Count);
Console.WriteLine(list.Contains(5));
Console.WriteLine(list.IndexOf(1));
list.Sort();
Console.WriteLine(string.Join(",", list));
list.Reverse();
Console.WriteLine(string.Join(",", list));Output
3,1,5,9,2
5
True
1
1,2,3,5,9
9,5,3,2,1Dictionary
C#
var ages = new Dictionary<string, int>
{
["张三"] = 28,
["李四"] = 32,
};
ages["王五"] = 25;
Console.WriteLine(ages["张三"]);
// 安全取值
if (ages.TryGetValue("赵六", out var age))
{
Console.WriteLine(age);
}
else
{
Console.WriteLine("不存在");
}
// 不存在才添加
ages.TryAdd("张三", 99); // 已存在,返回 false,不覆盖
// 遍历
foreach (var (name, a) in ages)
{
Console.Write($"{name}={a} ");
}
Console.WriteLine();
// 删除
ages.Remove("王五");
Console.WriteLine(ages.Count);Output
28
不存在
张三=28 李四=32 王五=25
2dict[key] throws when the key is missing; TryGetValue returns false. In hot loops use TryGetValue — one lookup instead of ContainsKey plus an index (two hashes).HashSet 与集合运算
C#
var a = new HashSet<int> { 1, 2, 3, 4 };
var b = new HashSet<int> { 3, 4, 5, 6 };
a.IntersectWith(b);
Console.WriteLine("交集 " + string.Join(",", a.OrderBy(x => x)));
var c = new HashSet<int> { 1, 2, 3, 4 };
c.UnionWith(b);
Console.WriteLine("并集 " + string.Join(",", c.OrderBy(x => x)));
var d = new HashSet<int> { 1, 2, 3, 4 };
d.ExceptWith(b);
Console.WriteLine("差集 " + string.Join(",", d.OrderBy(x => x)));
// 去重
var nums = new[] { 1, 1, 2, 3, 3 };
Console.WriteLine(new HashSet<int>(nums).Count);Output
交集 3,4
并集 1,2,3,4,5,6
差集 1,2
3Queue 与 Stack
C#
var q = new Queue<string>();
q.Enqueue("a");
q.Enqueue("b");
q.Enqueue("c");
Console.WriteLine(q.Peek()); // 看队首
Console.WriteLine(q.Dequeue()); // 出队
Console.WriteLine(q.Count);
var s = new Stack<int>();
s.Push(1);
s.Push(2);
s.Push(3);
Console.WriteLine(s.Peek());
Console.WriteLine(s.Pop());
Console.WriteLine(s.Count);
// 安全版本
if (q.TryDequeue(out var item)) Console.WriteLine(item);Output
a
a
2
3
3
2
bDequeue/Pop throw InvalidOperationException when empty. .NET Core 2.0+ adds TryDequeue/TryPop — use them when emptiness is uncertain.
LINQ 常用
C#
var people = new List<(string Name, int Age)>
{
("张三", 28), ("李四", 32), ("王五", 25), ("赵六", 32)
};
var adults = people.Where(p => p.Age >= 28).Select(p => p.Name).ToList();
Console.WriteLine(string.Join(",", adults));
Console.WriteLine(people.Count(p => p.Age == 32));
Console.WriteLine(people.Max(p => p.Age));
Console.WriteLine(people.Average(p => p.Age).ToString("F1"));
var grouped = people.GroupBy(p => p.Age);
foreach (var g in grouped)
{
Console.WriteLine($"{g.Key}: {string.Join(",", g.Select(x => x.Name))}");
}
var byAge = people.OrderByDescending(p => p.Age).ThenBy(p => p.Name).First();
Console.WriteLine(byAge.Name);Output
张三,李四,赵六
2
32
29.3
28: 张三
32: 李四,赵六
25: 王五
李四LINQ is lazy: Where/Select only build a query; ToList, Count or foreach executes it. Enumerating twice runs it twice — materialise with ToList() to reuse.
选哪个
| 集合 | 查找 | 有序 | 允许重复 |
|---|---|---|---|
List<T> | 索引 O(1),值 O(n) | 插入顺序 | 是 |
Dictionary<K,V> | 键 O(1) | 无序 | 键唯一 |
HashSet<T> | 值 O(1) | 无序 | 否 |
SortedDictionary | 键 O(log n) | 按键排序 | 键唯一 |
Queue<T> | 先进先出 | FIFO | 是 |
Stack<T> | 后进先出 | LIFO | 是 |
LinkedList<T> | 插入删除 O(1) | 链表顺序 | 是 |
List
C#
var list = new List<int> { 3, 1, 4 };
list.Add(5);
list.AddRange(new[] { 9, 2 });
list.Insert(0, 0);
list.Remove(4);
list.RemoveAt(0);
Console.WriteLine(string.Join(",", list));
Console.WriteLine(list.Count);
Console.WriteLine(list.Contains(5));
Console.WriteLine(list.IndexOf(1));
list.Sort();
Console.WriteLine(string.Join(",", list));
list.Reverse();
Console.WriteLine(string.Join(",", list));Output
3,1,5,9,2
5
True
1
1,2,3,5,9
9,5,3,2,1Dictionary
C#
var ages = new Dictionary<string, int>
{
["张三"] = 28,
["李四"] = 32,
};
ages["王五"] = 25;
Console.WriteLine(ages["张三"]);
// 安全取值
if (ages.TryGetValue("赵六", out var age))
{
Console.WriteLine(age);
}
else
{
Console.WriteLine("不存在");
}
// 不存在才添加
ages.TryAdd("张三", 99); // 已存在,返回 false,不覆盖
// 遍历
foreach (var (name, a) in ages)
{
Console.Write($"{name}={a} ");
}
Console.WriteLine();
// 删除
ages.Remove("王五");
Console.WriteLine(ages.Count);Output
28
不存在
张三=28 李四=32 王五=25
2dict[key] 在键不存在时抛异常,TryGetValue 返回 false。遍历大量数据时用 TryGetValue——它只查一次,而 ContainsKey + 索引是两次哈希。HashSet 与集合运算
C#
var a = new HashSet<int> { 1, 2, 3, 4 };
var b = new HashSet<int> { 3, 4, 5, 6 };
a.IntersectWith(b);
Console.WriteLine("交集 " + string.Join(",", a.OrderBy(x => x)));
var c = new HashSet<int> { 1, 2, 3, 4 };
c.UnionWith(b);
Console.WriteLine("并集 " + string.Join(",", c.OrderBy(x => x)));
var d = new HashSet<int> { 1, 2, 3, 4 };
d.ExceptWith(b);
Console.WriteLine("差集 " + string.Join(",", d.OrderBy(x => x)));
// 去重
var nums = new[] { 1, 1, 2, 3, 3 };
Console.WriteLine(new HashSet<int>(nums).Count);Output
交集 3,4
并集 1,2,3,4,5,6
差集 1,2
3Queue 与 Stack
C#
var q = new Queue<string>();
q.Enqueue("a");
q.Enqueue("b");
q.Enqueue("c");
Console.WriteLine(q.Peek()); // 看队首
Console.WriteLine(q.Dequeue()); // 出队
Console.WriteLine(q.Count);
var s = new Stack<int>();
s.Push(1);
s.Push(2);
s.Push(3);
Console.WriteLine(s.Peek());
Console.WriteLine(s.Pop());
Console.WriteLine(s.Count);
// 安全版本
if (q.TryDequeue(out var item)) Console.WriteLine(item);Output
a
a
2
3
3
2
bDequeue / Pop 在空集合上会抛 InvalidOperationException。.NET Core 2.0+ 提供了 TryDequeue / TryPop,不确定非空时用它们。
LINQ 常用
C#
var people = new List<(string Name, int Age)>
{
("张三", 28), ("李四", 32), ("王五", 25), ("赵六", 32)
};
var adults = people.Where(p => p.Age >= 28).Select(p => p.Name).ToList();
Console.WriteLine(string.Join(",", adults));
Console.WriteLine(people.Count(p => p.Age == 32));
Console.WriteLine(people.Max(p => p.Age));
Console.WriteLine(people.Average(p => p.Age).ToString("F1"));
var grouped = people.GroupBy(p => p.Age);
foreach (var g in grouped)
{
Console.WriteLine($"{g.Key}: {string.Join(",", g.Select(x => x.Name))}");
}
var byAge = people.OrderByDescending(p => p.Age).ThenBy(p => p.Name).First();
Console.WriteLine(byAge.Name);Output
张三,李四,赵六
2
32
29.3
28: 张三
32: 李四,赵六
25: 王五
李四LINQ 是惰性的:Where / Select 只是构建查询,ToList / Count / foreach 才真正执行。所以对同一个查询枚举两次会执行两次——需要复用就先 ToList()。