C# 数组(Array)是固定长度的连续内存块,适合已知规模、高频只读 场景;泛型列表(List<T>)是动态扩容 的封装结构,适合规模不确定、需频繁增删场景。两者索引访问均为 O(1),但底层开销与内存行为差异显著。
关键差异对比
表格
| 维度 | 数组 (T[]) |
列表 (List<T>) |
|---|---|---|
| 长度可变性 | ❌ 创建后不可变(需 Array.Resize 重建) |
✅ 自动扩容(默认策略:容量不足时翻倍) |
| 内存布局 | 纯连续内存,无额外字段,GC 压力小 | 内部含数组 + Count/Capacity 字段;未设容量时易产生冗余空间 |
| 访问性能 | 极快(IL 指令 ldelem 直接寻址,缓存友好) |
略慢(含方法调用、边界检查;Debug 模式更明显) |
| 增删操作 | 不支持原生增删;中间插入/删除需手动移动元素(O(n)) | Add() 均摊 O(1)(尾部);Insert/RemoveAt 为 O(n)(需移位) |
| 功能丰富度 | 基础操作(索引、Length、Array.Sort 等) |
丰富方法(Add/Remove/Find/Contains/LINQ 兼容等) |
| 类型安全 | 泛型原生支持,值类型零装箱 | 泛型实现,值类型零装箱(优于非泛型 ArrayList) |
| 典型场景 | 图像像素缓冲、配置常量表、高频遍历固定数据集 | 用户输入收集、运行时对象列表、迭代开发中数据量不确定场景 |
性能与优化要点
- 内存占用 :未预设容量的
List<T>因多次扩容,峰值内存可达同规模数组的5~10 倍 ;建议用new List<T>(expectedSize)预分配。 - GC 影响 :短生命周期大
List易触发 Gen0 GC;局部小数组常被 JIT 优化至栈上(配合Span<T>可进一步零分配)。 - 多维支持 :数组原生支持多维(
int[,]);List<T>仅一维,嵌套列表效率低且内存不连续。 - 接口抽象 :对外暴露集合时优先用
IReadOnlyList<T>或Span<T>(.NET Core 2.1+),避免泄露具体实现。
选择决策流
- 数据量是否创建时已知且永不变更 ? → 是:用数组
- 是否需频繁在中间插入/删除 ? → 是 :评估
LinkedList<T>或保留List<T>(小数据量可接受 O(n)) - 是否需动态增长 + 丰富操作 + LINQ ? → 是 :用
List<T>(务必预设容量) - 极端性能敏感(如游戏帧循环、高频交易)且数据固定? → 优先数组 ,必要时结合
Span<T>避免拷贝。
注:
Array.Resize本质是新建数组 + 复制,非真正"扩容";避免在高频路径中反复创建集合实例。