如何克服从IEnumerable <T>创建List <T>的开销?

Joa*_*nge 1 .net c# linq performance ienumerable

我正在使用一些LINQ select东西来创建一些返回的集合IEnumerable<T>.

在我的情况下,我需要一个List<T>,所以我将结果传递给List<T>构造函数来创建一个.

我想知道这样做的开销.我的收藏中的项目通常是数百万,所以我需要考虑这个.

我假设,如果IEnumerable<T>包含ValueTypes,它是最糟糕的表现.

我对吗?Ref类型怎么样?无论哪种方式,还有List<T>.Add一百万次通话的费用,对吗?

有办法解决这个问题吗?像我可以使用扩展方法"重载"LINQ Select等方法吗?

Ree*_*sey 6

最好避免使用列表.如果你可以使用IEnumerable <T>保持你的来电者,你会省去一些麻烦.

LINQ的ToList()将使用您的枚举,并使用List <T>(IEnumerable <T>)构造函数直接从它构造一个新的List <T>.这与自己制作列表相同,性能明智(尽管LINQ也进行了空检查).

如果您自己添加元素,请使用AddRange方法而不是Add.ToList()与AddRange非常相似(因为它使用了带有IEnumerable <T>的构造函数),在这种情况下,这通常是你最好的选择.

  • @Joan Venge:不.这是运行时检查.采用IEnumerable的List <T>构造函数执行转换为ICollection <T>(即:ICollection <T> coll = source作为ICollection <T>),如果它是ICollection <T>,则直接使用Count属性要预分配,然后使用CopyTo而不是枚举所有元素.如果源是列表(或任何其他ICollection <T>实现),这可能会使其更快. (2认同)

Jon*_*eet 6

没有,有元素类型存在价值的类型没有特别的惩罚,假设你使用IEnumerable<T>的替代IEnumerable.你不会得到任何拳击.

如果您事先确实知道结果的大小(Select可能不会导致结果),您可能需要考虑使用该大小的缓冲区创建列表,然后使用AddRange添加值.否则,列表必须在每次填充缓冲区时调整其缓冲区大小.

例如,而不是做:

Foo[] foo = new Foo[100];
IEnumerable<string> query = foo.Select(foo => foo.Name);
List<string> queryList = new List<string>(query);
Run Code Online (Sandbox Code Playgroud)

你可能会这样做:

Foo[] foo = new Foo[100];
IEnumerable<string> query = foo.Select(x => x.Name);
List<string> queryList = new List<string>(foo.Length);
queryList.AddRange(query);
Run Code Online (Sandbox Code Playgroud)

知道调用Select将生成与原始查询源相同长度的序列,但据我所知,执行环境中没有任何内容具有该信息.

  • 列表*本身*不会因为您更改列表中引用引用的对象的内容而被修改.这样说吧 - 如果有人有房屋地址列表,如果有人在房子里添加了一些家具,*列表*会改变吗? (2认同)
  • 不,在每种情况下都会复制序列中的值 - 对于值类型,这些值是实际数据(数字等).对于参考类型,他们是参考.请参见http://pobox.com/~skeet/csharp/references.html (2认同)