C# 集合(Collection)

`List`、`Dictionary`、`HashSet`、`Queue`、`Stack`、以及 LINQ。

Intermediate
Key Points按访问模式选集合:按索引用 `List`,按键用 `Dictionary`,去重用 `HashSet`。

选哪个

CollectionLookupOrderDuplicates
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,1

Dictionary

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 
2
dict[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
3

Queue 与 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
b

Dequeue/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.

Friends & Partners