📦
选择原则:数量固定且需要最小开销用数组;主线程动态集合优先 List<T>;进入 Jobs/Burst 或需要非托管连续内存时使用 NativeArray<T>

为什么连续存储重要

数组、List<T>NativeArray<T> 都把元素放在连续内存中。顺序遍历时缓存命中率高,按索引访问是 O(1),因此它们通常是 Unity 热路径的首选。
区别主要在于:长度是否可变、内存由谁管理、能否进入 Job,以及是否允许托管引用。

Array:固定长度

数组适合出生点、固定技能槽、预计算曲线采样等规模稳定的数据。它的长度创建后不变,但数组中的元素仍可修改。

List:动态长度

List<T> 内部仍然是数组。容量不足时会申请更大的数组并复制元素,所以提前估计容量可以减少扩容与 GC:
对顺序无要求时,删除中间元素可使用“末尾覆盖”避免搬移:
这种删除是 O(1),但会改变顺序。

NativeArray:Jobs/Burst 的连续容器

NativeArray<T> 存储非托管数据,可以被 Job 安全访问,并能被 Burst 优化:
调度与释放:
实际项目应尽量复用 Persistent 容器,而不是每帧申请。任何读取或释放都必须遵守 JobHandle 依赖。

复杂度

操作
Array
List
NativeArray
按索引访问
O(1)
O(1)
O(1)
尾部追加
不可变长
均摊 O(1)
不可变长
中间插入/删除
需手动复制
O(n)
需手动处理
Jobs/Burst
不可直接并行写
不支持
支持

Unity 常见坑

  • 不要在 foreach 遍历 List<T> 时增删它;使用倒序索引或延迟操作队列。
  • List.Contains 是 O(n)。循环中反复查找唯一元素,应考虑 HashSet<T>
  • Unity 默认不能直接序列化 IReadOnlyList<T> 接口字段;序列化私有数组或 List,再暴露只读视图。
  • NativeArray 不会由 GC 自动回收;必须 Dispose
  • Allocator.Temp 只适合非常短的生命周期;跨帧 Job 使用 TempJob,长期数据使用 Persistent
  • 不要只凭理论替换容器;用 Profiler 和 Memory Profiler 验证热点。

什么时候选谁

  • 数组:Inspector 固定配置、固定槽位、查表数据。
  • List:敌人注册表、任务列表、动态 UI 数据。
  • NativeArray:大批量数值计算、Job 输入输出、Burst 热循环。

系列导航

  1. 常用数据结构:Unity/C# 选择指南
  1. Array / List / NativeArray:连续内存容器
  1. Dictionary / HashSet / NativeHashMap:哈希容器
  1. Stack / Queue / Binary Heap:顺序与优先级
  1. 链表:Unity 中何时使用,何时避开
  1. Tree:层级、搜索与决策
  1. Graph:寻路、依赖与关系网络
  1. 空间索引:Grid / Spatial Hash / Quadtree / Octree
  1. Unity Native Containers:Jobs / Burst 下的数据结构

参考资料