AddRange 是原地合并两个同类型 List 的最直接高效方式,时间复杂度 O(n),但要求非空且类型严格一致;Concat + ToList 生成新列表,支持任意 IEnumerable,适合不可变场景。

用 AddRange 合并两个 List 最直接
如果目标是把第二个列表所有元素追加到第一个列表末尾(原地修改),AddRange 是最常用、最高效的方式。它不创建新对象,只做内存拷贝,时间复杂度 O(n)。
- 要求两个
List类型必须完全一致(比如都是List或List) - 第一个列表不能为空引用,否则会抛出
NullReferenceException - 如果第二个列表为
null,AddRange会直接抛出ArgumentNullException,需提前判空
示例:
List<string> list1 = new List<string> { "a", "b" };
List<string> list2 = new List<string> { "c", "d" };
list1.AddRange(list2); // list1 现在是 ["a","b","c","d"]
</string></string></string></string>
用 Concat + ToList 创建新列表
当不能修改原列表、或需要函数式风格(不改变输入)时,Concat 是标准做法。注意它返回的是 IEnumerable,必须调用 ToList() 才能得到新的 List。
-
Concat不会去重,也不排序,就是简单拼接 - 性能上比
AddRange稍差:涉及枚举器创建 + 新数组分配,但对中小规模数据几乎无感 - 支持任意
IEnumerable,不限于List(比如数组、LINQ 查询结果也能接)
示例:
List<int> a = new List<int> { 1, 2 };
List<int> b = new List<int> { 3, 4 };
List<int> merged = a.Concat(b).ToList(); // 新列表,a 和 b 均未变
</int></int></int></int></int>
合并时遇到类型不匹配怎么办
常见错误是试图合并 List 和 List(即使 Dog 继承自 Animal),编译会失败:Argument 1: cannot convert from 'System.Collections.Generic.List。
- 根本原因是
List是不变的(invariant),不支持协变 - 解决办法:显式转换成基类序列,例如
dogList.Cast().Concat(animalList) - 或者统一用接口:把两个列表都声明为
IEnumerable再拼接 - 若需保持类型安全且避免装箱,可考虑用泛型方法约束
where T : Animal
别忽略容量(Capacity)带来的性能陷阱
用 AddRange 向一个已满容量的 List 追加大量元素时,内部数组会反复扩容(通常是翻倍),触发多次内存分配和复制,影响性能。
- 合并前可预估总大小,手动设置
list1.Capacity = list1.Count + list2.Count - 如果合并操作频繁,建议封装成工具方法,自动处理容量预分配
-
Concat方式不受此影响——它内部由 LINQ 自行管理缓冲,但最终ToList()仍会按需分配一次完整数组
实际项目中,几十项以内基本不用管容量;但处理上千条日志、配置项或 API 返回数据时,这个细节就容易拖慢吞吐。










