【C#】List<T> 和 T[] 的选择
本文最后更新于:7 天前
前言
在开始之前,我相信大家都知道 List<T>
是动态数组,可增删元素,必要时自动扩容;而 T[]
是真正意义上的数组,元素个数在创建时就确定了。但尽管如此,我发现大家在开发过程中即使遇到数据数量固定的场景也更喜欢用 List<T>
而不是 T[]
。本文就带讲一下这两种数组的区别,以及合理的使用。注意为了区分,后文将 List<T>
称为 列表
,T[]
称为 数组
。
数组
先说一个冷知识,数组类型实际上有基类的,叫 System.Array
,这个基类提供了很多有关操作数组的方法 (包括静态方法) 和属性,感兴趣的可以去了解一下,不是本文的重点。
数组得益于其元素数量固定的特性,性能很高,但灵活性很小,一旦创建后就不能增删元素了,但并不意味着它就没有用武之地。我建议,在任何情况下,特别是不需要增删元素的情况下,优先考虑数组。
什么?你不会创建数组?这可能是一个很奇葩的原因,因为曾有人跟我说数组没有像列表那样的 Add
或 AddRange
方法,写起来非常不爽,还说要用数组的时候都是从列表或者是其他类型的集合上进行 ToArray
得到的,但就因此,所以他更喜欢用列表。
如果屏幕前的你也是因为这个原因的话,那就必须要纠正这种误区了,这里就演示两种创建创建数组的方法。
方法一:从元素创建
如果已知各元素,我们可以在初始化集合的时候就将元素写进去。比如现要将 1
,2
,3
等元素塞入一个 int[]
数组,应该怎么操作?
如果你在使用 C# 12 之前的版本,就这样:
1 |
|
如果你使用的是 C# 12 及以上的版本,可以使用 集合表达式:
1 |
|
方法二:先创建后填充
若已知元素,但不方便直接把元素写到初始化语句里面。通常适用于将其他数组或集合转换为数组的场景,就可以这样操作,而不是用列表挨个去 Add
。
Tip: 常见集合元素个数的获取
数组:Length 属性
列表:Count 属性
ICollection<T>:Count 属性
IEnumerable<T>:Count() 方法
假设现有 Person[]
或者是 List<Person>
之类的集合,Person
里有一个属性用来存储编号,现在我要收集这些编号并封装为 int[]
,此时就可以采用先建后填的方式:
1 |
|
如果你熟悉 LINQ,其实还可以简化很多,但推荐数据量少的情况下使用,数据太多的话还是自己写比较高效,尽管 LINQ 内部实现也和上面差不多,但是委托也会消耗一定的性能。
1 |
|
列表
列表的内部其实也是数组。以图为证:链接
当你调用 Add 的时候,它在:
能装就装,不能装就扩 (Resize),扩完就装
怎么扩?看看实现:
在 Capacity 的 setter 中,肉眼可见它根据新的容量创建了一个空数组,然后调用 Array.Copy 将之前的元素全部复制进去,这样就相当于扩容了。
虽然性能上肯定不如数组,但为了实现增删功能,做出这些牺牲是值得的。实际开发中,这些牺牲虽然微不足道,但也需引起重视,特别是数据量超大的时候,频繁的扩容肯定是不合适的。
你可能会说,列表也可以设置初始容量,这样不就和数组一样了吗?我的建议是,只要你不增删,坚决不用列表。当你指定初始容量时,背后也是创建了一个相同容量的数组,都这样了还不如直接自己创建一个数组。
数组使用场景
这里说说部分有必要使用数组的场景,除此之外请自行考虑清楚。
- JSON 配置实体类
- 从一种集合中批量提取数据 (比如收集用户在 ListView 中存放的数据)
- 编译时就能确认不再增删的情况
总结
一句话,需要动态增删就 List<T>,只存只读就用 T[]。