最快合并方式是Concat(),返回延迟执行的IEnumerable视图拼接;Array.Copy()适合精确控制内存;MemoryExtensions.Concat()(.NET 6+)实现Span零分配拼接;Union()等去重方法不可用于合并。

用 Concat() 最快,但要注意返回的是 IEnumerable<t></t>
直接调用 Concat() 是最轻量的合并方式,不复制底层数据,只做延迟执行的“视图拼接”。适合只读遍历、链式处理的场景。
- 如果后续要多次访问或修改,必须调用
ToArray()或ToList()实例化,否则每次遍历都重新计算 -
Concat()要求两个数组类型兼容(如int[]和long[]会编译失败),不能自动装箱/拆箱 - 对大数组做多次
Concat().Concat().Concat()容易造成嵌套迭代器开销,性能反而不如一次性分配新数组
示例:
int[] a = {1, 2};<br>int[] b = {3, 4};<br>var combined = a.Concat(b).ToArray(); // → {1, 2, 3, 4}
用 Array.Copy() 合并更可控,适合已知长度且需复用缓冲区
当你要精确控制内存分配、避免 LINQ 开销,或需要把结果写入已有目标数组时,Array.Copy() 是底层可靠的选择。
- 目标数组必须预先分配足够空间,否则抛出
ArgumentException:“源数组中复制的元素数大于目标数组中从索引开始的可用空间” - 注意第三个参数是“复制长度”,不是“源数组长度”;第四个参数是“目标起始索引”,别写成
0就默认覆盖开头 - 比
Concat()多一次内存分配和拷贝,但 CPU 友好、无装箱、无迭代器状态机
示例:
int[] a = {1, 2};<br>int[] b = {3, 4};<br>int[] result = new int[a.Length + b.Length];<br>Array.Copy(a, 0, result, 0, a.Length);<br>Array.Copy(b, 0, result, a.Length, b.Length);
用 MemoryExtensions.Concat()(.NET 6+)零分配拼接 Span<t></t>
如果你操作的是栈上数据(比如 stackalloc 数组)、或在高性能热路径中反复合并小数组,Span<t></t> 的 Concat() 是唯一真正不堆分配的方式。
- 返回的是
ReadOnlySpan<t></t>,生命周期受限于原始数据作用域,不能跨方法返回或存为字段 - 不支持引用类型数组的直接拼接(
string[]不行),只适用于值类型或固定大小结构体 - 编译时报错常见: “Cannot convert from 'int[]' to 'ReadOnlySpan
'”——记得加 .AsSpan()
示例:
int[] a = {1, 2};<br>int[] b = {3, 4};<br>ReadOnlySpan<int> spanA = a.AsSpan();<br>ReadOnlySpan<int> spanB = b.AsSpan();<br>ReadOnlySpan<int> combined = spanA.Concat(spanB); // 零分配
别用 Union() 或 Distinct() 代替合并
这是新手高频误用点:Union() 本质是去重后的集合并集,顺序不保证,还会排序(内部用哈希或比较器),完全不是“连接”语义。
- 输入
{1,2}和{2,3},Union()返回{1,2,3},丢掉重复的2,也丢掉原始顺序 - 字符串数组用
Union()还可能因大小写或文化敏感比较导致意外过滤 - 哪怕加了
OrderBy()也救不回原始位置信息——这不是合并,是重构
错误示范:
var bad = a.Union(b).ToArray(); // 不是连接,别这么用
合并的本质是“保持原有顺序、不丢元素、不改内容”,选方法前先盯住这三点。类型匹配、目标容量、生命周期管理——这三个地方一松手,后面就全是隐性 bug。


















