List:日常 80% 用它
var list = new List<int> { 1, 2, 3 };
list.Add(4);
list.AddRange(new[] { 5, 6 });
list.Insert(0, 0); // 头插
list.Remove(3); // 删第一个 == 3 的
list.RemoveAt(0); // 按索引删
list[0] = 99; // 改
int len = list.Count; // 长度(不是 Length)
bool has = list.Contains(2);
int idx = list.IndexOf(2);
list.Clear();
List<T> 是动态数组——底层是 T[],自动扩容。
Dictionary<K, V>:哈希字典
var d = new Dictionary<string, int>
{
["alice"] = 30,
["bob"] = 25,
};
d["charlie"] = 28; // 加 / 改
// 安全读
if (d.TryGetValue("alice", out var age))
{
Console.WriteLine(age);
}
// 不安全读(不存在 KeyNotFoundException)
int x = d["nobody"]; // ❌ 抛异常
// 检查
d.ContainsKey("alice");
d.Remove("alice");
// 遍历
foreach (var (k, v) in d) { ... }
foreach (var kv in d) { Console.WriteLine($"{kv.Key} = {kv.Value}"); }
永远用 TryGetValue——比"先 ContainsKey 再 d[key]"快一半(少查一次哈希)。
HashSet:集合(去重 + 快速 Contains)
var set = new HashSet<int> { 1, 2, 3 };
set.Add(2); // 不重复加
set.Contains(2); // O(1)
set.UnionWith(new[] { 3, 4 }); // 并集
set.IntersectWith(new[] { 2, 3 }); // 交集
set.ExceptWith(new[] { 2 }); // 差集
Contains 是 O(1)(List 的是 O(n))——大量查找用 HashSet。
Queue / Stack
var q = new Queue<int>();
q.Enqueue(1); q.Enqueue(2);
int first = q.Dequeue(); // 1(FIFO)
q.Peek(); // 看头不弹
var s = new Stack<int>();
s.Push(1); s.Push(2);
int top = s.Pop(); // 2(LIFO)
数组 T[]
int[] arr = new int[5]; // 默认 0
int[] arr2 = { 1, 2, 3, 4, 5 };
int[,] grid = new int[3, 4]; // 二维(不是嵌套)
int[][] jagged = { new[] {1,2}, new[] {3,4,5} }; // 锯齿
Array.Sort(arr2);
Array.Reverse(arr2);
arr2.Length;
数组固定大小、性能最好。不需要变长用数组比 List 省。
集合接口层级
IEnumerable<T> 最基础:能 foreach
└─ ICollection<T> 能 Add / Remove / Count
└─ IList<T> 能按索引访问
└─ List<T>
└─ IReadOnlyCollection<T>
└─ IReadOnlyList<T>
└─ IDictionary<K, V>
└─ Dictionary<K, V>
参数用更窄的接口:
// ❌ 限定 List 太死
void Sum(List<int> items) { ... }
// ✅ 接受任何能遍历的
void Sum(IEnumerable<int> items) { ... }
只要能 foreach 就行 → 数组、List、Dictionary.Values 都能传。
ImmutableCollection(不可变集合)
using System.Collections.Immutable;
var list = ImmutableList<int>.Empty
.Add(1)
.Add(2)
.Add(3);
var list2 = list.Add(4); // 不改 list,返回新的
Console.WriteLine(list.Count); // 3
Console.WriteLine(list2.Count); // 4
需要 NuGet:dotnet add package System.Collections.Immutable。
适合配置 / DI 容器 / 多线程共享数据——免锁。
ReadOnly 视图
var list = new List<int> { 1, 2, 3 };
IReadOnlyList<int> ro = list.AsReadOnly(); // 包装,不能改
// 或直接返回 IReadOnlyList<int>,调用方拿到不能改的引用
不是真不变——底层 list 改了 ro 也变。是"接口契约"层面的只读。
Span / Memory(性能场景)
Span<int> span = stackalloc int[10]; // 栈上分配
for (int i = 0; i < 10; i++) span[i] = i * i;
// 切片不复制
Span<int> sub = span.Slice(2, 4);
Span<T> 是栈上的"指针 + 长度"——零分配、零复制。极性能场景用(解析、加密、网络包处理)。
不能存字段、不能跨 await——专用工具,不是日常 List 的替代。
字典选哪个
| 用 | |
|---|---|
| 一般 | Dictionary<K, V> |
| 多线程 | ConcurrentDictionary<K, V> |
| 不可变 | ImmutableDictionary<K, V> |
| 只读视图 | IReadOnlyDictionary<K, V> |
| 顺序很重要(按插入序) | Dictionary 自 .NET Core 起保插入序 |
| 按 key 排序 | SortedDictionary<K, V> |
LINQ 直接转
var arr = new[] { 3, 1, 4, 1, 5 };
List<int> sorted = arr.OrderBy(x => x).ToList();
Dictionary<int, int> hist = arr.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
HashSet<int> uniq = arr.ToHashSet();
LINQ 方法 + .ToX() 几乎是数据加工的核心模式——下篇细讲。
→ 下一篇 LINQ