在LINQ查询中调用ToList()或ToArray()会更好吗?

Fra*_*ger 488 .net linq performance

我经常遇到我想在我声明它的地方评估查询的情况.这通常是因为我需要多次迭代它并且计算起来很昂贵.例如:

string raw = "...";
var lines = (from l in raw.Split('\n')
             let ll = l.Trim()
             where !string.IsNullOrEmpty(ll)
             select ll).ToList();
Run Code Online (Sandbox Code Playgroud)

这很好用.但是,如果我不打算修改结果,那么我不妨打电话给ToArray()而不是ToList().

我想知道是否ToArray()通过第一次调用实现,ToList()因此内存效率低于仅调用ToList().

我疯了吗?我应该只是打电话ToArray()- 安全且安全地知道内存不会被分配两次吗?

Jar*_*Par 340

除非您只需要一个数组来满足其他约束,否则您应该使用它ToList.在大多数情况下ToArray会分配更多的内存ToList.

两者都使用数组进行存储,但ToList具有更灵活的约束.它需要数组至少与集合中元素的数量一样大.如果数组较大,那不是问题.但是,ToArray需要将数组的大小精确地调整为元素的数量.

为了满足这种约束,ToArray通常会进行一次分配ToList.一旦它有一个足够大的数组,它就会分配一个完全正确大小的数组,并将元素复制回该数组.唯一能避免这种情况的是当数组的增长算法恰好与需要存储的元素数量一致时(绝对是少数).

编辑

有几个人问我在List<T>数值中有多余的未使用内存的后果.

这是一个有效的问题.如果创建的集合是长期存在的,在创建后永远不会被修改并且很有可能在Gen2堆中着陆,那么您可能最好先采取额外的分配ToArray.

总的来说,虽然我发现这是罕见的情况.更常见的是看到很多ToArray调用立即传递给其他短暂的内存使用,在这种情况下ToList显然更好.

这里的关键是剖析,剖析,然后再详细介绍一些.

  • 另一方面,为创建数组的legwork分配的额外内存是否有资格进行垃圾回收,而List的额外开销是否仍然存在?我说保持简单.如果您需要添加或删除元素,那么就有一个工具.如果你不这样做,那就有一个不同的工具.使用有意义的那个.如果以后,你会发现内存和性能问题,*就是这个*,改变它. (11认同)
  • @JaredPar我不明白`ToArray`如果需要确切的位置大小可以分配更多的内存,其中`ToList <>`显然有它的自动备用位置.(autoincrease) (8认同)
  • @RoyiNamir因为ToArray首先使用开销进行ToList样式的分配,然后进行额外的精确大小分配. (5认同)
  • @AnthonyPegram 是的,这是一个有效的考虑因素。如果该值用于长期存储,不会被修改,并且可能会进入第 2 代,那么您现在最好支付额外的分配,而不是污染第 2 代堆。IME 虽然我很少看到这个。看到 ToArray 被立即传递给另一个短暂的 LINQ 查询更为常见。 (2认同)
  • @AnthonyPegram我更新了我的答案,包括讨论的这一方 (2认同)
  • 查看“ToArray”和“ToList”的源代码,我不明白您如何得出“ToList”更好的结论。两者都以 4 的数组开始,并在需要时将其大小加倍。一旦完成,“ToList”将不会修剪多余的部分,但有一个额外的方法。然而,“ToArray”将确保它仅使用所需的内存量。因此,“ToArray”又使用了一次分配,但“ToList”在最坏的情况下可能会分配太多空间,即最后一个元素需要新的分配。 (2认同)

mqp*_*mqp 166

性能差异将是微不足道的,因为它List<T>是作为动态大小的阵列实现的.调用ToArray()(使用内部Buffer<T>类来增长数组)或ToList()(调用List<T>(IEnumerable<T>)构造函数)将最终成为将它们放入数组并增长数组直到它适合所有数组的问题.

如果您希望具体确认这一事实,请查看Reflector中相关方法的实现 - 您将看到它们归结为几乎完全相同的代码.

  • 如果预先知道Count,则性能相同.但是,如果事先不知道Count,那么`ToArray()`和`ToList()`之间的唯一区别是前者必须修剪多余的,这涉及复制整个数组,而后者不削减多余,但平均使用25%的内存.如果数据类型是一个大的`struct`,这只会产生影响.只是值得深思. (129认同)
  • @EldritchConundrum 25%来自这个逻辑:如果项目数量未知,那么调用`ToList`或`ToArray`将从创建一个小缓冲区开始.当填充缓冲区时,它会使缓冲区的容量加倍并继续.由于容量总是加倍,因此未使用的缓冲区将始终在0%和50%之间. (9认同)
  • @AndyClaw两个`List`和`Buffer`都会检查`ICollection`,在这种情况下,性能将是相同的. (3认同)
  • 我遇到的一个有趣的事实是,对于在投影中使用通过组连接定义的组而导致的相关查询会导致Linq to SQL添加另一个子查询以检索该组的计数.我假设这意味着在这些情况下,在检索项目之前将知道集合的大小,因此可以直接创建精确大小的数组,这将节省处理和内存资源,同时实现结果. (2认同)
  • @ScottRippey我只是从IEnumerable源中查找新List的源代码,它检查IEnumerable是否是ICollection,如果是,那么它首先从Count属性中分配一个具有所需精确大小的数组,所以这个将是ToList()肯定会更快的情况.一个完整的答案可能包括这个事实,虽然我不认为这是最常见的情况. (2认同)

Tyr*_*rrz 79

外面是 2020 年,每个人都在使用 .NET Core 3.1,所以我决定使用 Benchmark.NET 运行一些基准测试。

TL;DR:ToArray() 在性能方面更好,如果您不打算改变集合,则可以更好地传达意图。


    [MemoryDiagnoser]
    public class Benchmarks
    {
        [Params(0, 1, 6, 10, 39, 100, 666, 1000, 1337, 10000)]
        public int Count { get; set; }

        public IEnumerable<int> Items => Enumerable.Range(0, Count);

        [Benchmark(Description = "ToArray()", Baseline = true)]
        public int[] ToArray() => Items.ToArray();

        [Benchmark(Description = "ToList()")]
        public List<int> ToList() => Items.ToList();

        public static void Main() => BenchmarkRunner.Run<Benchmarks>();
    }

Run Code Online (Sandbox Code Playgroud)

结果是:


    BenchmarkDotNet=v0.12.0, OS=Windows 10.0.14393.3443 (1607/AnniversaryUpdate/Redstone1)
    Intel Core i5-4460 CPU 3.20GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
    Frequency=3124994 Hz, Resolution=320.0006 ns, Timer=TSC
    .NET Core SDK=3.1.100
      [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
      DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT


    |    Method | Count |          Mean |       Error |      StdDev |        Median | Ratio | RatioSD |   Gen 0 | Gen 1 | Gen 2 | Allocated |
    |---------- |------ |--------------:|------------:|------------:|--------------:|------:|--------:|--------:|------:|------:|----------:|
    | ToArray() |     0 |      7.357 ns |   0.2096 ns |   0.1960 ns |      7.323 ns |  1.00 |    0.00 |       - |     - |     - |         - |
    |  ToList() |     0 |     13.174 ns |   0.2094 ns |   0.1958 ns |     13.084 ns |  1.79 |    0.05 |  0.0102 |     - |     - |      32 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     1 |     23.917 ns |   0.4999 ns |   0.4676 ns |     23.954 ns |  1.00 |    0.00 |  0.0229 |     - |     - |      72 B |
    |  ToList() |     1 |     33.867 ns |   0.7350 ns |   0.6876 ns |     34.013 ns |  1.42 |    0.04 |  0.0331 |     - |     - |     104 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     6 |     28.242 ns |   0.5071 ns |   0.4234 ns |     28.196 ns |  1.00 |    0.00 |  0.0280 |     - |     - |      88 B |
    |  ToList() |     6 |     43.516 ns |   0.9448 ns |   1.1949 ns |     42.896 ns |  1.56 |    0.06 |  0.0382 |     - |     - |     120 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    10 |     31.636 ns |   0.5408 ns |   0.4516 ns |     31.657 ns |  1.00 |    0.00 |  0.0331 |     - |     - |     104 B |
    |  ToList() |    10 |     53.870 ns |   1.2988 ns |   2.2403 ns |     53.415 ns |  1.77 |    0.07 |  0.0433 |     - |     - |     136 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    39 |     58.896 ns |   0.9441 ns |   0.8369 ns |     58.548 ns |  1.00 |    0.00 |  0.0713 |     - |     - |     224 B |
    |  ToList() |    39 |    138.054 ns |   2.8185 ns |   3.2458 ns |    138.937 ns |  2.35 |    0.08 |  0.0815 |     - |     - |     256 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   100 |    119.167 ns |   1.6195 ns |   1.4357 ns |    119.120 ns |  1.00 |    0.00 |  0.1478 |     - |     - |     464 B |
    |  ToList() |   100 |    274.053 ns |   5.1073 ns |   4.7774 ns |    272.242 ns |  2.30 |    0.06 |  0.1578 |     - |     - |     496 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   666 |    569.920 ns |  11.4496 ns |  11.2450 ns |    571.647 ns |  1.00 |    0.00 |  0.8688 |     - |     - |    2728 B |
    |  ToList() |   666 |  1,621.752 ns |  17.1176 ns |  16.0118 ns |  1,623.566 ns |  2.85 |    0.05 |  0.8793 |     - |     - |    2760 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1000 |    796.705 ns |  16.7091 ns |  19.8910 ns |    796.610 ns |  1.00 |    0.00 |  1.2951 |     - |     - |    4064 B |
    |  ToList() |  1000 |  2,453.110 ns |  48.1121 ns |  65.8563 ns |  2,460.190 ns |  3.09 |    0.10 |  1.3046 |     - |     - |    4096 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1337 |  1,057.983 ns |  20.9810 ns |  41.4145 ns |  1,041.028 ns |  1.00 |    0.00 |  1.7223 |     - |     - |    5416 B |
    |  ToList() |  1337 |  3,217.550 ns |  62.3777 ns |  61.2633 ns |  3,203.928 ns |  2.98 |    0.13 |  1.7357 |     - |     - |    5448 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() | 10000 |  7,309.844 ns | 160.0343 ns | 141.8662 ns |  7,279.387 ns |  1.00 |    0.00 | 12.6572 |     - |     - |   40064 B |
    |  ToList() | 10000 | 23,858.032 ns | 389.6592 ns | 364.4874 ns | 23,759.001 ns |  3.26 |    0.08 | 12.6343 |     - |     - |   40096 B |

    // * Hints *
    Outliers
      Benchmarks.ToArray(): Default -> 2 outliers were removed (35.20 ns, 35.29 ns)
      Benchmarks.ToArray(): Default -> 2 outliers were removed (38.51 ns, 38.88 ns)
      Benchmarks.ToList(): Default  -> 1 outlier  was  removed (64.69 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (67.02 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (130.08 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  detected (541.82 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (7.82 us)

    // * Legends *
      Count     : Value of the 'Count' parameter
      Mean      : Arithmetic mean of all measurements
      Error     : Half of 99.9% confidence interval
      StdDev    : Standard deviation of all measurements
      Median    : Value separating the higher half of all measurements (50th percentile)
      Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
      RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
      Gen 0     : GC Generation 0 collects per 1000 operations
      Gen 1     : GC Generation 1 collects per 1000 operations
      Gen 2     : GC Generation 2 collects per 1000 operations
      Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
      1 ns      : 1 Nanosecond (0.000000001 sec)

Run Code Online (Sandbox Code Playgroud)

  • @ErikOvegård @Tyrrrz @Jonas @neonblitzer 我将基准代码修改为: `public IEnumerable&lt;int&gt; Items =&gt; Enumerable.Range(0, Count).Where(i =&gt; i % 17 == 0);` 并且在大多数情况下在大多数情况下,“ToList”的速度大约快 10%,并且分配量更少。 (7认同)
  • 谢谢你。所选择的答案只是一个论点,并假设该论点之后的结果。为了科学地做到这一点,并且作为额外的奖励,知道差异有多大,只有一种真正的方法可以知道。 (6认同)
  • 如果您不打算改变集合,我认为可以使用“ToImmutableArray()”(来自 System.Collections.Immutable 包)更好地显示意图 (5认同)
  • 这没有考虑到“RangeIterator.ToArray”和“RangeIterator.ToList”实现知道集合的最终大小。对于任何更复杂的 LINQ 表达式(例如,使用“Where”),无法知道其大小,并且必须动态调整结果集合的大小。因此,这个答案仅适用于一小部分现实案例。 (4认同)
  • 使用相同长度的数组重复运行该测试是否会受到影响?这意味着 [LargeArrayBuilder](https://github.com/dotnet/runtime/blob/1d9e50cb4735df46d3de0cee5791e97295eaf588/src/libraries/Common/src/System/Collections/Generic/LargeArrayBuilder.SpeedOpt 中可以使用正确大小的缓冲区。 CS) (3认同)

Jep*_*sen 44

(七年后......)

其他(好)答案集中在将发生的微观性能差异上.

此信息仅仅是一个补充提语义差别之间存在IEnumerator<T>(产生由阵列T[])相比于由一个返回List<T>.

通过示例最佳说明:

IList<int> source = Enumerable.Range(1, 10).ToArray();  // try changing to .ToList()

foreach (var x in source)
{
  if (x == 5)
    source[8] *= 100;
  Console.WriteLine(x);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将运行,没有异常并产生输出:

1
2
3
4
5
6
7
8
900
10

这表明IEnumarator<int>由an返回的数据int[]不会跟踪自创建枚举器以来数组是否已被修改.

请注意,我将局部变量声明sourceIList<int>.通过这种方式,我确保C#编译器不会将foreach语句优化为等同于for (var idx = 0; idx < source.Length; idx++) { /* ... */ }循环的东西.如果我使用C#编译器可能会这样做var source = ...;.在我当前版本的.NET框架中,这里使用的实际枚举器是非公共引用类型,System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]但当然这是一个实现细节.

现在,如果我改变.ToArray().ToList(),我只得到:

1
2
3
4
5

随后是一个System.InvalidOperationException爆炸性的说法:

收集被修改; 枚举操作可能无法执行.

在这种情况下,底层枚举器是public mutable value-type System.Collections.Generic.List`1+Enumerator[System.Int32](IEnumerator<int>在这种情况下,盒子装在盒子里,因为我使用IList<int>).

总之,由枚举器生成的枚举器List<T>会跟踪列表在枚举期间是否发生更改,而生成的枚举T[]器则不会.因此,在.ToList()和之间选择时要考虑这种差异.ToArray().

人们经常添加一个额外的 .ToArray().ToList()绕过一个集合来跟踪它是否在枚举器的生命周期中被修改.

(如果有人想知道如何List<>跟踪上收集是否被修改,有一个私人领域_version在这个类,这是每次改变的List<>更新.)

  • 非常有用,但这只是数组和列表之间的区别,不是吗。这不是特定于 ToArray 与 ToList 实现的。并不是要批评,只是以防万一对其他人有帮助。 (3认同)

EMP*_*EMP 27

我同意@mquander的观点,即性能差异应该是微不足道的.但是,我想确定它的基准,所以我做了 - 而且它是微不足道的.

Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList  time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List

Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList  time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List
Run Code Online (Sandbox Code Playgroud)

每个源数组/ List都有1000个元素.所以你可以看到时间和内存的差异可以忽略不计.

我的结论是:你也可以使用ToList(),因为a List<T>提供了比数组更多的功能,除非几个字节的内存对你很重要.

  • -1当ToArray()`和`ToList()`的行为提供了一个`ICollection <T>`参数时,它们的行为差别太大 - 它们只进行一次分配和一次复制操作.`List <T>`和`Array`都实现了`ICollection <T>`,因此你的基准测试根本无效. (23认同)
  • 列表<T> .ToList ???? 有什么意义?你最好尝试给它一个IEnumerable,它没有实现ICollection接口. (12认同)
  • 我想确保我只测量`ToList`或`ToArray`调用的时间而不是任何`IEnumerable`的枚举.List <T> .ToList()仍然创建一个新的List <T> - 它不仅仅是"返回此". (8认同)

Guf*_*ffa 19

内存将始终分配两次 - 或接近该内存.由于无法调整数组大小,因此两种方法都将使用某种机制来收集不断增长的集合中的数据.(好吧,List是一个不断增长的集合.)

List使用数组作为内部存储,并在需要时将容量加倍.这意味着平均2/3的项目至少重新分配一次,其中一半重新分配至少两次,其中一半重新分配至少三次,依此类推.这意味着每个项目平均重新分配1.3次,这不是很大的开销.

还要记住,如果要收集字符串,集合本身只包含对字符串的引用,字符串本身不会重新分配.


Vit*_*kov 19

ToList()如果您在IEnumerable<T>(例如,来自ORM)上使用它通常是首选.如果在开头不知道序列的长度,则ToArray()创建像List这样的动态长度集合,然后将其转换为数组,这需要额外的时间.

  • 在这种情况下,我已经决定可读性胜过性能.我现在只在我希望继续添加元素时才使用ToList.在所有其他情况下(大多数情况下),我使用ToArray.但感谢您的投入! (24认同)
  • 查看ILSpy,`Enumerable.ToArray()`调用`new Buffer <TSource>(source).ToArray()`.在Buffer构造函数中,如果源实现ICollection,则它调用source.CopyTo(items,0),然后.ToArray()直接返回内部items数组.因此在这种情况下没有转换需要额外的时间.如果源没有实现ICollection,则ToArray将导致数组副本,以便从阵列末尾修剪额外的未使用位置,如Scott Rippey上面的评论所述. (5认同)
  • @FrankKrueger可读性有何不同? (4认同)

Sco*_*pey 15

Edit: The last part of this answer is not valid. However, the rest is still useful information, so I'll leave it.

I know this is an old post, but after having the same question and doing some research, I have found something interesting that might be worth sharing.

First, I agree with @mquander and his answer. He is correct in saying that performance-wise, the two are identical.

However, I have been using Reflector to take a look at the methods in the System.Linq.Enumerable extensions namespace, and I have noticed a very common optimization.
Whenever possible, the IEnumerable<T> source is cast to IList<T> or ICollection<T> to optimize the method. For example, look at ElementAt(int).

Interestingly, Microsoft chose to only optimize for IList<T>, but not IList. It looks like Microsoft prefers to use the IList<T> interface.

System.Array只实现IList,因此它不会受益于任何这些扩展优化.
因此,我认为最佳做法是使用该.ToList()方法.
如果您使用任何扩展方法,或将列表传递给另一个方法,则可能会对其进行优化IList<T>.

  • 我做了一个测试,发现了令人惊讶的事情.数组DOES实现IList <T>!使用Reflector分析System.Array只显示IList,ICollection,IEnumerable的继承链,但使用运行时反射我发现string []有一个继承链IList,ICollection,IEnumerable,IList <string>,ICollection <string >,IEnumerable <string>.因此,我没有比@mquander更好的答案! (16认同)

naw*_*fal 12

你应该立足自己的决定去ToListToArray基于什么理想的设计选择.如果您想要一个只能通过索引迭代和访问的集合,请选择ToArray.如果你想要在以后添加和删除集合的其他功能而没有太多的麻烦,那么做一个ToList(不是真的你不能添加到数组,但这通常不是正确的工具).

如果性能很重要,您还应该考虑哪些操作更快.实际上,你不会打电话ToListToArray一百万次,但可能会在获得的收集上工作一百万次.在这方面[]比较好,因为List<>[]有一些开销.请参阅此线程以进行效率比较:哪一个更有效:List <int>或int []

在我不久前的测试中,我发现ToArray速度更快了.而且我不确定测试有多么偏差.但性能差异是如此微不足道,只有当您在循环中运行这些查询数百万次时才能注意到这一点.

  • 是 - 如果编译器知道您正在迭代数组(而不是IEnumerable <>),它可以显着优化迭代. (2认同)

Erd*_*tur 12

一个非常晚的答案,但我认为这将有助于谷歌.

他们在使用linq创建时都很糟糕.如有必要,它们都实现相同的代码来调整缓冲区大小.ToArray内部使用一个类转换IEnumerable<>为数组,通过分配一个包含4个元素的数组.如果这还不够,可以通过创建一个新的数组,将当前大小加倍,并将当前数组复制到该数组,从而使其大小增加一倍.最后,它会分配一个新的项目数量.如果您的查询返回129个元素,那么ToArray将进行6次分配和内存复制操作以创建256个元素数组,而不是另一个129要返回的数组.对于记忆效率这么多.

ToList执行相同的操作,但它会跳过最后一次分配,因为您可以在将来添加项目.列表不关心它是从linq查询创建还是手动创建.

创建列表更好的内存,但更糟糕的是cpu,因为列表是一个通用的解决方案,每个操作都需要范围检查,除了.net的内部范围检查数组.

因此,如果您将遍历结果集太多次,那么数组是好的,因为它意味着比列表更少的范围检查,并且编译器通常优化数组以进行顺序访问.

如果在创建容量参数时指定容量参数,则列表的初始化分配会更好.在这种情况下,假设您知道结果大小,它将仅分配一次数组.ToListlinq没有指定提供它的重载,所以我们必须创建我们的扩展方法,创建一个具有给定容量的列表然后使用List<>.AddRange.

要完成这个答案,我必须写下面的句子

  1. 最后,您可以使用ToArray或ToList,性能不会如此不同(请参阅@EMP的答案).
  2. 您正在使用C#.如果您需要性能,那么不要担心编写高性能代码,但担心不编写糟糕的性能代码.
  3. 始终以x64为目标,以获得高性能代码.AFAIK,x64 JIT基于C++编译器,并做一些有趣的事情,如尾递归优化.
  4. 使用4.5,您还可以享受配置文件引导优化和多核JIT.
  5. 最后,您可以使用async/await模式更快地处理它.

  • 内存中唯一的冗余数据是数组的内容,它是一个指针列表(在本例中).一百万个64位指针需要多达8 MB的内存,这与他们指向的一百万个对象相比毫无意义.200只是一个数字,它有机会减少调整大小调用次数最多5次.是的,我们无法帮助它.我们没有更好的选择.我没有更好的解决方案,但这并不意味着我不能说出问题所在. (2认同)

Str*_*ior 12

我发现人们在这里做的其他基准测试缺乏,所以这里是我的解决方案.如果您发现我的方法有问题,请告诉我.

/* This is a benchmarking template I use in LINQPad when I want to do a
 * quick performance test. Just give it a couple of actions to test and
 * it will give you a pretty good idea of how long they take compared
 * to one another. It's not perfect: You can expect a 3% error margin
 * under ideal circumstances. But if you're not going to improve
 * performance by more than 3%, you probably don't care anyway.*/
void Main()
{
    // Enter setup code here
    var values = Enumerable.Range(1, 100000)
        .Select(i => i.ToString())
        .ToArray()
        .Select(i => i);
    values.GetType().Dump();
    var actions = new[]
    {
        new TimedAction("ToList", () =>
        {
            values.ToList();
        }),
        new TimedAction("ToArray", () =>
        {
            values.ToArray();
        }),
        new TimedAction("Control", () =>
        {
            foreach (var element in values)
            {
                // do nothing
            }
        }),
        // Add tests as desired
    };
    const int TimesToRun = 1000; // Tweak this as necessary
    TimeActions(TimesToRun, actions);
}


#region timer helper methods
// Define other methods and classes here
public void TimeActions(int iterations, params TimedAction[] actions)
{
    Stopwatch s = new Stopwatch();
    int length = actions.Length;
    var results = new ActionResult[actions.Length];
    // Perform the actions in their initial order.
    for (int i = 0; i < length; i++)
    {
        var action = actions[i];
        var result = results[i] = new ActionResult { Message = action.Message };
        // Do a dry run to get things ramped up/cached
        result.DryRun1 = s.Time(action.Action, 10);
        result.FullRun1 = s.Time(action.Action, iterations);
    }
    // Perform the actions in reverse order.
    for (int i = length - 1; i >= 0; i--)
    {
        var action = actions[i];
        var result = results[i];
        // Do a dry run to get things ramped up/cached
        result.DryRun2 = s.Time(action.Action, 10);
        result.FullRun2 = s.Time(action.Action, iterations);
    }
    results.Dump();
}

public class ActionResult
{
    public string Message { get; set; }
    public double DryRun1 { get; set; }
    public double DryRun2 { get; set; }
    public double FullRun1 { get; set; }
    public double FullRun2 { get; set; }
}

public class TimedAction
{
    public TimedAction(string message, Action action)
    {
        Message = message;
        Action = action;
    }
    public string Message { get; private set; }
    public Action Action { get; private set; }
}

public static class StopwatchExtensions
{
    public static double Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Restart();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.Elapsed.TotalMilliseconds;
    }
}
#endregion
Run Code Online (Sandbox Code Playgroud)

您可以在此处下载LINQPad脚本.

结果: ToArray vs ToList性能

调整上面的代码,你会发现:

  1. 处理较小的阵列时,差异不太明显. 更多迭代,但更小的数组
  2. 在处理ints而不是strings 时,差异不太明显.
  3. 使用大structs而不是strings需要花费更多时间,但并没有真正改变比率.

这与最高投票答案的结论一致:

  1. 除非您的代码经常生成许多大型数据列表,否则您不太可能注意到性能差异.(每个创建1000个100K字符串列表时只有200ms的差异.)
  2. ToList() 一直运行得更快,如果你不打算长时间坚持结果,那将是一个更好的选择.

更新

@JonHanna指出,根据Select它的实现,一个ToList()或一个ToArray()实现可以提前预测产生的集合的大小.替换.Select(i => i)上面的代码会Where(i => true) 产生非常类似的结果,并且无论.NET实现如何都更有可能这样做.

基准使用Where而不是Select


Fre*_*nge 7

这是一个古老的问题 - 但是为了绊倒它的用户的利益,还有"Memoizing"Enumerable的替代方案 - 它具有缓存和停止Linq语句的多个枚举的效果,这就是ToArray()即使从不使用列表或数组的集合属性,也会使用ToList().

Memoize在RX/System.Interactive lib中可用,并在此解释: 更多LINQ with System.Interactive

(来自Bart De'Smet的博客,如果您与Linq一起使用很多对象,这是一个强烈推荐的读物)


wes*_*ton 6

一种选择是添加您自己的返回readonly 的 ICollection<T>扩展方法。当您不想使用数组/列表的索引属性或从列表中添加/删除时,这可能比使用ToListor更好。ToArray

public static class EnumerableExtension
{
    /// <summary>
    /// Causes immediate evaluation of the linq but only if required.
    /// As it returns a readonly ICollection, is better than using ToList or ToArray
    /// when you do not want to use the indexing properties of an IList, or add to the collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumerable"></param>
    /// <returns>Readonly collection</returns>
    public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable)
    {
        //if it's already a readonly collection, use it
        var collection = enumerable as ICollection<T>;
        if ((collection != null) && collection.IsReadOnly)
        {
            return collection;
        }
        //or make a new collection
        return enumerable.ToList().AsReadOnly();
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试:

[TestClass]
public sealed class EvaluateLinqTests
{
    [TestMethod]
    public void EvalTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResult = list.Select(i => i);
        var linqResultEvaluated = list.Select(i => i).Evaluate();
        list.Clear();
        Assert.AreEqual(0, linqResult.Count());
        //even though we have cleared the underlying list, the evaluated list does not change
        Assert.AreEqual(3, linqResultEvaluated.Count());
    }

    [TestMethod]
    public void DoesNotSaveCreatingListWhenHasListTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //list is not readonly, so we expect a new list
        Assert.AreNotSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasReadonlyListTest()
    {
        var list = new List<int> {1, 2, 3}.AsReadOnly();
        var linqResultEvaluated = list.Evaluate();
        //list is readonly, so we don't expect a new list
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasArrayTest()
    {
        var list = new[] {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //arrays are readonly (wrt ICollection<T> interface), so we don't expect a new object
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantAddToResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Add(4);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantRemoveFromResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Remove(1);
    }
}
Run Code Online (Sandbox Code Playgroud)


qaq*_*111 5

老问题,但始终有新提问者。

根据System.Linq.Enumerable的来源,ToList只需返回 a new List(source),而ToArray使用 a则new Buffer<T>(source).ToArray()返回 a T[]

关于内存分配:

IEnumerable<T>在唯一对象上运行时,ToArray请比 多分配一次内存ToList。但大多数情况下你不必关心它,因为GC会在需要时进行垃圾收集。

关于运行时效率:

质疑这个问题的人可以在自己的机器上运行以下代码,你就会得到答案。

class PersonC
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

struct PersonS
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

class PersonT<T> : IEnumerable<T>
{
    private List<T> items;
    public PersonT(IEnumerable<T> init)
    {
        items = new List<T>(init);
    }

    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
}

private IEnumerable<PersonC> C(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonC
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private IEnumerable<PersonS> S(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonS
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private void MakeLog(string test, List<long> log) =>
    Console.WriteLine("{0} {1} ms -> [{2}]",
        test,
        log.Average(),
        string.Join(", ", log)
    );

private void Test1(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    MakeLog("C.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test2(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC1 = new PersonT<PersonC>(C(count));
    var dataS1 = new PersonT<PersonS>(S(count));

    MakeLog("C1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test3(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC2 = (ICollection<PersonC>) new List<PersonC>(C(count));
    var dataS2 = (ICollection<PersonS>) new List<PersonS>(S(count));

    MakeLog("C2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void TestMain()
{
    const int times = 100;
    const int count = 1_000_000 + 1;
    Test1(times, count);
    Test2(times, count);
    Test3(times, count);
}
Run Code Online (Sandbox Code Playgroud)

我在我的机器上得到了这些结果:

第1组:

C.ToList 761.79 ms -> [775, 755, 759, 759, 756, 759, 765, 750, 757, 762, 759, 754, 757, 753, 763, 753, 759, 756, 768, 754, 763, 757, 757, 777, 780, 758, 754, 758, 762, 754, 758, 757, 763, 758, 760, 754, 761, 755, 764, 847, 952, 755, 747, 763, 760, 758, 754, 763, 761, 758, 750, 764, 757, 763, 762, 756, 753, 759, 759, 757, 758, 779, 765, 760, 760, 756, 760, 756, 755, 764, 759, 753, 757, 760, 752, 764, 758, 760, 758, 760, 755, 761, 751, 753, 761, 762, 761, 758, 759, 752, 765, 756, 760, 755, 757, 753, 760, 751, 755, 779]
C.ToArray 782.56 ms -> [783, 774, 771, 771, 773, 774, 775, 775, 772, 770, 771, 774, 771, 1023, 975, 772, 767, 776, 771, 779, 772, 779, 775, 771, 775, 773, 775, 771, 765, 774, 770, 781, 772, 771, 781, 762, 817, 770, 775, 779, 769, 774, 763, 775, 777, 769, 777, 772, 775, 778, 775, 771, 770, 774, 772, 769, 772, 769, 774, 775, 768, 775, 769, 774, 771, 776, 774, 773, 778, 769, 778, 767, 770, 787, 783, 779, 771, 768, 805, 780, 779, 767, 773, 771, 773, 785, 1044, 853, 775, 774, 775, 771, 770, 769, 770, 776, 770, 780, 821, 770]
S.ToList 704.2 ms -> [687, 702, 709, 691, 694, 710, 696, 698, 700, 694, 701, 719, 706, 694, 702, 699, 699, 703, 704, 701, 703, 705, 697, 707, 691, 697, 707, 692, 721, 698, 695, 700, 704, 700, 701, 710, 700, 705, 697, 711, 694, 700, 695, 698, 701, 692, 696, 702, 690, 699, 708, 700, 703, 714, 701, 697, 700, 699, 694, 701, 697, 696, 699, 694, 709, 1068, 690, 706, 699, 699, 695, 708, 695, 704, 704, 700, 695, 704, 695, 696, 702, 700, 710, 708, 693, 697, 702, 694, 700, 706, 699, 695, 706, 714, 704, 700, 695, 697, 707, 704]
S.ToArray 742.5 ms -> [742, 743, 733, 745, 741, 724, 738, 745, 728, 732, 740, 727, 739, 740, 726, 744, 758, 732, 744, 745, 730, 739, 738, 723, 745, 757, 729, 741, 736, 724, 744, 756, 739, 766, 737, 725, 741, 742, 736, 748, 742, 721, 746, 1043, 806, 747, 731, 727, 742, 742, 726, 738, 746, 727, 739, 743, 730, 744, 753, 741, 739, 746, 728, 740, 744, 734, 734, 738, 731, 747, 736, 731, 765, 735, 726, 740, 743, 730, 746, 742, 725, 731, 757, 734, 738, 741, 732, 747, 744, 721, 742, 741, 727, 745, 740, 730, 747, 760, 737, 740]

C1.ToList 32.34 ms -> [35, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 32, 31, 31, 31, 31, 30, 32, 31, 31, 31, 31, 32, 30, 31, 31, 31, 30, 32, 31, 31, 31, 36, 31, 31, 31, 32, 30, 31, 32, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 33, 32, 31, 32, 31, 31, 33, 31, 31, 31, 31, 31, 32, 31, 32, 31, 34, 38, 68, 42, 79, 33, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 32, 31, 32, 31, 31, 31, 32, 33, 33, 31, 31]
C1.ToArray 56.32 ms -> [57, 56, 59, 54, 54, 55, 56, 57, 54, 54, 55, 55, 57, 56, 59, 57, 56, 58, 56, 56, 54, 56, 57, 55, 55, 55, 57, 58, 57, 58, 55, 55, 56, 55, 57, 56, 56, 59, 56, 56, 56, 56, 58, 56, 57, 56, 56, 57, 56, 55, 56, 56, 56, 59, 56, 56, 56, 55, 55, 54, 55, 54, 57, 56, 56, 56, 55, 55, 56, 56, 56, 59, 56, 56, 57, 56, 57, 56, 56, 56, 56, 62, 55, 56, 56, 56, 69, 57, 58, 56, 57, 58, 56, 57, 56, 56, 56, 56, 56, 56]
S1.ToList 88.69 ms -> [96, 90, 90, 89, 91, 88, 89, 90, 96, 89, 89, 89, 90, 90, 90, 89, 90, 90, 89, 90, 89, 91, 89, 91, 89, 91, 89, 90, 90, 89, 87, 88, 87, 88, 87, 87, 87, 87, 88, 88, 87, 87, 89, 87, 87, 87, 91, 88, 87, 86, 89, 87, 90, 89, 89, 90, 89, 87, 87, 87, 86, 87, 88, 90, 88, 87, 87, 92, 87, 87, 88, 88, 88, 86, 86, 87, 88, 87, 87, 87, 89, 87, 89, 87, 90, 89, 89, 89, 91, 89, 90, 89, 90, 88, 90, 90, 90, 88, 89, 89]
S1.ToArray 143.26 ms -> [130, 129, 130, 131, 133, 130, 131, 130, 135, 137, 130, 136, 132, 131, 130, 131, 132, 130, 132, 136, 130, 131, 157, 153, 194, 364, 176, 189, 203, 194, 189, 192, 183, 140, 142, 147, 145, 134, 159, 158, 142, 167, 130, 143, 145, 144, 160, 154, 156, 153, 153, 164, 142, 145, 137, 134, 145, 143, 142, 135, 133, 133, 135, 134, 134, 139, 139, 133, 134, 141, 133, 132, 133, 132, 133, 131, 135, 132, 133, 132, 128, 128, 130, 132, 129, 129, 129, 129, 129, 128, 134, 129, 129, 129, 129, 128, 128, 137, 130, 131]

C2.ToList 3.25 ms -> [5, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3]
C2.ToArray 3.37 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 4, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3]
S2.ToList 37.72 ms -> [38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 39, 37, 37, 39, 38, 37, 37, 37, 37, 39, 38, 37, 37, 38, 37, 38, 37, 37, 38, 37, 37, 37, 38, 37, 37, 36, 37, 38, 37, 39, 37, 39, 38, 37, 38, 38, 38, 38, 38, 38, 37, 38, 38, 38, 38, 38, 37, 38, 37, 37, 38, 37, 37, 39, 41, 37, 38, 38, 37, 37, 37, 37, 38, 37, 37, 37, 40, 37, 37, 37, 37, 39, 38]
S2.ToArray 38.86 ms -> [39, 37, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 38, 38, 38, 39, 37, 38, 38, 38, 38, 38, 37, 37, 38, 37, 37, 38, 38, 40, 38, 38, 38, 38, 38, 39, 38, 38, 39, 38, 38, 39, 38, 38, 40, 38, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 39, 37, 38, 38, 39, 71, 78, 37, 37, 37, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 38, 38, 38]
Run Code Online (Sandbox Code Playgroud)

第2组:

C.ToList 756.81 ms
C.ToArray 774.21 ms
S.ToList 709.7 ms
S.ToArray 753.51 ms

C1.ToList 32.06 ms
C1.ToArray 56.58 ms
S1.ToList 89.43 ms
S1.ToArray 132.85 ms

C2.ToList 3.45 ms
C2.ToArray 3.36 ms
S2.ToList 41.43 ms
S2.ToArray 40.84 ms
Run Code Online (Sandbox Code Playgroud)

第三组:

C.ToList 756.64 ms
C.ToArray 771.56 ms
S.ToList 705.42 ms
S.ToArray 749.59 ms

C1.ToList 31.45 ms
C1.ToArray 57.03 ms
S1.ToList 91.26 ms
S1.ToArray 129.77 ms

C2.ToList 3.26 ms
C2.ToArray 3.29 ms
S2.ToList 41.57 ms
S2.ToArray 40.69 ms
Run Code Online (Sandbox Code Playgroud)

第4组:

C.ToList 729.65 ms -> [749, 730, 721, 719, 723, 743, 721, 724, 727, 722, 716, 725, 723, 726, 718, 722, 731, 722, 723, 725, 723, 722, 728, 726, 728, 718, 726, 1088, 788, 737, 729, 710, 730, 728, 717, 723, 728, 721, 722, 728, 722, 736, 723, 729, 732, 724, 726, 727, 728, 728, 726, 726, 725, 727, 725, 728, 728, 718, 724, 725, 726, 724, 726, 729, 727, 722, 722, 725, 725, 728, 724, 727, 738, 717, 726, 723, 725, 725, 727, 724, 720, 726, 726, 723, 727, 730, 723, 721, 725, 727, 727, 733, 720, 722, 722, 725, 722, 725, 728, 726]
C.ToArray 788.36 ms -> [748, 740, 742, 797, 1090, 774, 781, 787, 784, 786, 786, 782, 781, 781, 784, 783, 783, 781, 783, 787, 783, 784, 775, 789, 784, 785, 778, 774, 781, 783, 786, 781, 780, 788, 778, 785, 777, 781, 786, 782, 781, 787, 782, 787, 784, 773, 783, 782, 781, 777, 783, 781, 785, 788, 777, 776, 784, 784, 783, 789, 778, 781, 791, 768, 779, 783, 781, 787, 786, 781, 784, 781, 785, 781, 780, 809, 1155, 780, 790, 789, 783, 776, 785, 783, 786, 787, 782, 782, 787, 777, 779, 784, 783, 776, 786, 775, 782, 779, 784, 784]
S.ToList 705.54 ms -> [690, 705, 709, 708, 702, 707, 703, 696, 703, 702, 700, 703, 700, 707, 705, 699, 697, 703, 695, 698, 707, 697, 711, 710, 699, 700, 708, 707, 693, 710, 704, 691, 702, 700, 703, 700, 705, 700, 703, 695, 709, 705, 698, 699, 709, 700, 699, 704, 691, 705, 703, 700, 708, 1048, 710, 706, 706, 692, 702, 705, 695, 701, 710, 697, 698, 706, 705, 707, 707, 695, 698, 704, 698, 699, 705, 698, 703, 702, 701, 697, 702, 702, 704, 703, 699, 707, 703, 705, 701, 717, 698, 695, 713, 696, 708, 705, 697, 699, 700, 698]
S.ToArray 745.01 ms -> [751, 743, 727, 734, 736, 745, 739, 750, 739, 750, 758, 739, 744, 738, 730, 744, 745, 739, 744, 750, 733, 735, 743, 731, 749, 748, 727, 746, 749, 731, 737, 803, 1059, 756, 769, 748, 740, 745, 741, 746, 749, 732, 741, 742, 732, 744, 746, 737, 742, 739, 733, 744, 741, 729, 746, 760, 725, 741, 764, 739, 750, 751, 727, 745, 738, 727, 735, 741, 720, 736, 740, 733, 741, 746, 731, 749, 756, 740, 738, 736, 732, 741, 741, 733, 741, 744, 736, 742, 742, 735, 743, 746, 729, 748, 765, 743, 734, 742, 728, 749]

C1.ToList 32.27 ms -> [36, 31, 31, 32, 31, 32, 31, 30, 32, 30, 30, 30, 34, 32, 31, 31, 31, 31, 31, 31, 31, 32, 38, 51, 68, 57, 35, 30, 31, 31, 30, 30, 33, 30, 31, 34, 31, 34, 32, 31, 31, 31, 31, 32, 30, 30, 31, 30, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 33, 31, 31, 32, 30, 30, 30, 30, 30, 33, 30, 33, 32, 31, 30, 31, 31, 32, 32, 31, 35, 31, 34, 31, 31, 32, 31, 31, 32, 31, 32, 31, 31, 35, 31, 31, 31, 31, 31, 32]
C1.ToArray 56.72 ms -> [58, 56, 57, 57, 59, 58, 58, 57, 56, 59, 57, 55, 55, 54, 56, 55, 56, 56, 57, 59, 56, 55, 58, 56, 55, 55, 55, 55, 58, 58, 55, 57, 57, 56, 57, 57, 57, 57, 59, 59, 56, 57, 56, 57, 57, 56, 57, 59, 58, 56, 57, 57, 57, 58, 56, 56, 59, 56, 59, 57, 57, 57, 57, 59, 57, 56, 57, 56, 58, 56, 57, 56, 57, 59, 55, 58, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 56, 56, 57, 56, 56, 57, 58, 57, 57, 57, 57, 57]
S1.ToList 90.72 ms -> [95, 90, 90, 89, 89, 89, 91, 89, 89, 87, 91, 89, 89, 89, 91, 89, 89, 89, 90, 89, 89, 90, 88, 89, 88, 90, 89, 90, 89, 89, 90, 90, 89, 89, 90, 91, 89, 91, 89, 90, 89, 89, 90, 91, 89, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 89, 90, 89, 91, 89, 90, 89, 90, 89, 90, 89, 96, 89, 90, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 87, 89, 90, 90, 91, 89, 91, 89, 89, 90, 91, 90, 89, 93, 144, 149, 90, 90, 89, 89, 89]
S1.ToArray 131.4 ms -> [130, 128, 127, 134, 129, 129, 130, 136, 131, 130, 132, 132, 133, 131, 132, 131, 133, 132, 130, 131, 132, 131, 130, 133, 133, 130, 130, 131, 131, 131, 132, 134, 131, 131, 132, 131, 132, 131, 134, 131, 131, 130, 131, 131, 130, 132, 129, 131, 131, 131, 132, 131, 133, 134, 131, 131, 132, 132, 131, 133, 131, 131, 130, 133, 131, 130, 134, 132, 131, 132, 132, 131, 131, 134, 131, 131, 132, 132, 131, 130, 138, 130, 130, 131, 132, 132, 130, 134, 131, 131, 132, 131, 130, 132, 133, 131, 131, 131, 130, 131]

C2.ToList 3.21 ms -> [4, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3]
C2.ToArray 3.22 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4]
S2.ToList 41.46 ms -> [42, 40, 41, 40, 42, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 39, 41, 41, 39, 40, 40, 43, 40, 39, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 43, 40, 43, 75, 76, 47, 39, 40, 40, 40, 40, 42, 40, 41, 40, 40, 40, 44, 41, 40, 42, 42, 40, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 42, 41, 40, 41, 41, 42, 42, 41, 40, 41, 41, 41, 41, 41, 40, 42, 40, 42, 41, 41, 41, 43, 41, 41, 41, 41, 42, 41]
S2.ToArray 41.14 ms -> [42, 41, 41, 40, 40, 40, 40, 41, 41, 42, 41, 42, 41, 41, 41, 42, 41, 41, 42, 41, 41, 41, 41, 41, 42, 40, 41, 40, 42, 40, 42, 41, 40, 42, 41, 41, 43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 41, 41, 41, 40, 42, 41, 41, 41, 41, 41, 40, 41, 41, 42, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, 42, 42, 42, 41, 45, 46, 41, 40, 41, 41, 42, 41, 41, 41, 41, 41, 41, 40, 41, 43, 40, 40, 40, 40, 43, 41]
Run Code Online (Sandbox Code Playgroud)

第5组:

C.ToList 757.06 ms -> [770, 752, 752, 751, 778, 763, 761, 763, 747, 758, 748, 747, 754, 749, 752, 753, 756, 762, 750, 753, 756, 749, 755, 757, 755, 756, 755, 744, 753, 758, 747, 751, 759, 751, 761, 755, 746, 752, 752, 749, 746, 752, 753, 755, 752, 755, 754, 754, 966, 937, 749, 759, 748, 747, 754, 749, 755, 750, 746, 754, 757, 752, 753, 745, 758, 755, 761, 753, 751, 755, 755, 752, 746, 756, 755, 746, 742, 751, 751, 749, 752, 751, 756, 756, 755, 742, 749, 754, 749, 756, 753, 751, 754, 752, 751, 754, 753, 749, 755, 756]
C.ToArray 772.8 ms -> [766, 772, 755, 763, 758, 767, 763, 762, 761, 768, 769, 763, 770, 757, 765, 760, 766, 759, 764, 761, 760, 777, 1102, 881, 759, 765, 758, 762, 772, 761, 758, 757, 765, 769, 769, 761, 762, 762, 763, 760, 770, 764, 760, 768, 758, 766, 763, 770, 769, 761, 764, 761, 761, 767, 761, 762, 764, 757, 765, 766, 767, 771, 753, 762, 769, 768, 759, 764, 764, 760, 763, 763, 763, 763, 763, 767, 761, 771, 760, 765, 760, 758, 768, 770, 751, 771, 767, 771, 765, 763, 760, 765, 765, 769, 767, 767, 1193, 774, 767, 764]
S.ToList 704.73 ms -> [682, 708, 705, 699, 705, 704, 695, 703, 702, 699, 701, 708, 699, 702, 703, 701, 701, 699, 701, 707, 707, 700, 701, 705, 700, 697, 706, 702, 701, 706, 699, 692, 702, 697, 707, 704, 697, 698, 699, 699, 702, 703, 698, 697, 702, 703, 702, 704, 694, 697, 707, 695, 711, 710, 700, 693, 703, 699, 699, 706, 698, 701, 703, 704, 698, 706, 700, 704, 701, 699, 702, 705, 694, 698, 709, 736, 1053, 704, 694, 700, 698, 696, 701, 700, 700, 706, 706, 692, 698, 707, 703, 695, 703, 699, 694, 708, 695, 694, 706, 695]
S.ToArray 744.17 ms -> [746, 740, 725, 740, 739, 731, 746, 760, 735, 738, 740, 734, 744, 748, 737, 744, 745, 727, 736, 738, 728, 743, 745, 735, 748, 760, 739, 748, 762, 742, 741, 747, 733, 746, 758, 742, 742, 741, 724, 744, 747, 727, 740, 740, 729, 742, 757, 741, 740, 742, 726, 739, 746, 1133, 749, 737, 730, 740, 747, 733, 747, 752, 731, 747, 742, 730, 741, 749, 731, 749, 74


Ste*_*eng 5

ToListAsync<T>()是优选的。

在实体框架 6 中,这两个方法最终都会调用相同的内部方法,但在最后ToArrayAsync<T>()调用,其实现为list.ToArray()

T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;
Run Code Online (Sandbox Code Playgroud)

因此ToArrayAsync<T>()有一些开销,因而ToListAsync<T>()是优选的。