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

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


















