You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ImmutableList不同创建方式的差异、性能及适用场景对比

ImmutableList 四种创建方式差异与选型对比

ImmutableList<T> 是.NET提供的不可变列表实现,底层用平衡二叉树存储,不是普通List那种连续数组结构,不同创建方式的内部逻辑差很多,性能和适用场景完全不一样,逐个拆解如下:

各方式实现原理与表现

  • 方式1:先构造普通List<T>再调用ToImmutableList()

    List<int> numbers = new List<int>(){0, 1, 2, 3};
    ImmutableList<int> immutableList = numbers.ToImmutableList();
    

    实现逻辑:ToImmutableList()本身没有特殊逻辑,内部就是直接调用ImmutableList.CreateRange()把当前List传进去,一次性遍历源数据构造平衡二叉树,不会额外拷贝数据做多余操作。
    性能表现:如果你手里本来就有一个填好数据的普通List,这个写法没有额外开销,树构造的效率和CreateRange完全一致;但要是你为了转不可变列表特意先new一个普通List填数据,等于平白多了一次临时List的内存分配和写入,纯浪费性能。
    适用场景:仅适合你已经持有现成普通List实例,需要转成不可变列表的场景,不要为了转不可变特意先建普通List。

  • 方式2:直接调用ImmutableList.Create<T>()传入固定个数元素

    ImmutableList<int> immutableList = ImmutableList.Create<int>(0, 1, 2, 3);
    

    实现逻辑:这是params参数重载,传少量元素的时候,内部会直接构造对应的树节点,不需要走批量集合遍历的逻辑;如果传的元素多,params会先生成一个临时数组存所有参数,再走和CreateRange一样的批量构造逻辑。
    性能表现:传个位数固定元素的时候性能最高,没有任何多余的临时集合开销,代码也最短;元素多了之后会多一次params数组的分配,性能比CreateRange差一点。
    适用场景:创建元素固定、数量少(一般5个以内)的不可变列表,比如固定常量集合、配置选项列表这类场景,写起来最简洁。

  • 方式3:直接调用ImmutableList.CreateRange<T>()传入源集合

    ImmutableList<int> immutableList = ImmutableList.CreateRange<int>(new List<int>() { 0, 1, 2, 3 });
    

    实现逻辑:这是所有批量创建不可变列表的底层统一入口,它会先判断传入的集合有没有已知长度(比如实现了ICollection<T>就有Count属性),如果有就直接按总长度一次性构造整棵平衡二叉树,避免逐元素添加时反复调整树结构带来的开销;如果传的是没有长度的惰性枚举(比如LINQ查询返回的IEnumerable),才会逐元素遍历构造。如果传入的源本身就是ImmutableList,它会直接返回原实例,连构造都省了。
    性能表现:如果你传入的是现成的、带已知长度的集合,这是批量创建性能最高的方式,没有多余的中间开销;但要是你为了调用这个方法特意new一个临时List传进去,那和方式1的多余开销一模一样,没必要。
    适用场景:你已经持有现成的任意IEnumerable<T>集合需要转不可变列表时,这是最标准的API,性能最优。

  • 方式4:使用Builder模式构造后转ImmutableList

    ImmutableList<int>.Builder builder = ImmutableList.CreateBuilder<int>();
    builder.AddRange(new List<int>() { 0, 1, 2, 3 });
    ImmutableList<int> immutableList = builder.ToImmutableList();
    

    实现逻辑:Builder内部维护的是一个普通的可变列表,不是二叉树结构,你在Builder上做Add、Remove、AddRange这类修改操作的时候,都是直接在这个可变列表上改,完全不需要做树平衡调整,等最后调用ToImmutableList()的时候,才会一次性把内部存的数据构造成平衡二叉树,返回最终的不可变列表实例。
    性能表现:如果你构造列表的过程中需要做多次增删改操作,Builder的性能比直接反复操作ImmutableList高几个数量级——毕竟直接改ImmutableList每次修改都会生成一棵新树,开销极大;但如果像示例代码里那样只调用一次AddRange就直接转不可变,性能反而比CreateRange差一点,多了一层Builder内部可变列表的维护开销。
    适用场景:构造不可变列表的过程中需要多次动态调整元素,比如循环添加元素、按条件增删元素、拼接多个数据源这类场景,不要在只需要一次性把现成集合转不可变的时候用Builder,属于画蛇添足。

选型总结

  • 少量固定元素:直接用ImmutableList.Create<T>(),简洁高效。
  • 已有现成集合需要转换:直接用ToImmutableList()或者CreateRange<T>(),二者底层逻辑一致,选写着顺手的就行,是批量场景里性能最高的写法。
  • 构造过程需要多次动态修改元素:必须用Builder,能避免大量无意义的树构造开销。
  • 永远不要为了转不可变列表特意new一个临时普通List,完全是多余的性能浪费。

内容的提问来源于stack exchange,提问作者M.Jaskuski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 18:39:55