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