关于.net:在LINQ查询中调用ToList()或ToArray()会更好吗?

Is it better to call ToList() or ToArray() in LINQ queries?

我经常遇到这样的情况:我想在声明查询的地方对其进行eval。这通常是因为我需要多次迭代它,并且计算起来很昂贵。例如:

1
2
3
4
5
6
string raw ="...";
var lines = (from l in raw.Split('
')
             let ll = l.Trim()
             where !string.IsNullOrEmpty(ll)
             select ll).ToList();

这个很好用。但是如果我不想修改结果,那么我可以调用ToArray(),而不是ToList()

不过,我想知道ToArray()是否是通过首先调用ToList()来实现的,因此它的内存效率比仅仅调用ToList()要低。

我疯了吗?我应该打电话给ToArray()-安全可靠地知道内存不会分配两次吗?


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

两者都使用阵列进行存储,但ToList具有更灵活的约束。它需要数组至少与集合中元素的数目相同。如果数组更大,这不是问题。然而,ToArray需要将数组的大小精确到元素的数量。

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

编辑

有几个人问过我,在List值中有多余的未使用内存的后果。

这是一个值得关注的问题。如果创建的集合寿命长,在创建后从不进行修改,并且有很高的机会登录到gen2堆中,那么您最好预先获得ToArray的额外分配。

总的来说,虽然我发现这是一个更罕见的情况。很多ToArray调用会立即传递给其他短暂的内存使用,这种情况下,ToList明显更好。

这里的关键是分析,分析,然后分析更多。


由于List是作为一个动态大小的数组实现的,因此性能差异将是微不足道的。调用ToArray()类(它使用内部Buffer类来增长数组)或ToList()类(它调用List(IEnumerable)构造函数)最终将成为将它们放入数组并增长数组直到全部适合它们的问题。

如果您希望对这一事实进行具体的确认,请查看Reflector中所讨论方法的实现——您将看到它们归结为几乎相同的代码。


(七年后……)

其他一些(好的)答案集中在微观性能的差异上。

本文只是对数组(T[]产生的IEnumeratorList返回的IEnumerator之间语义差异的补充。

最好举例说明:

1
2
3
4
5
6
7
8
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);
}

上述代码将毫无例外地运行并生成输出:

1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
900
10

这表明,由int[]返回的IEnumarator并不跟踪自创建枚举器以来是否修改了数组。

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

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

1
2
3
4
5
1
2
3
4
5

接着是一个System.InvalidOperationException爆炸说:

Collection was modified; enumeration operation may not execute.

在这种情况下,底层枚举器是公共可变值类型System.Collections.Generic.List`1+Enumerator[System.Int32](由于我使用IList),因此在这种情况下,它被装箱在IEnumerator框中)。

综上所述,List生成的枚举器跟踪枚举过程中列表是否发生变化,而T[]生成的枚举器不发生变化。因此,在选择.ToList().ToArray()时要考虑这一区别。

人们经常添加一个额外的.ToArray().ToList(),以避免跟踪集合是否在枚举器的生命周期中被修改。

(如果有人想知道List<>是如何跟踪集合是否被修改的,那么这个类中有一个私有字段_version,每当List<>被更新时,它都会被更改。)


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

1
2
3
4
5
6
7
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

每个源数组/列表都有1000个元素。所以你可以看到时间和记忆的差异是可以忽略的。

我的结论是:您也可以使用tolist(),因为List提供的功能比数组更多,除非几个字节的内存对您真的很重要。


如果您在IEnumerable上使用它(例如从ORM),通常首选ToList()。如果开始时序列的长度未知,那么ToArray()会创建类似list的动态长度集合,然后将其转换为数组,这需要额外的时间。


内存总是会被分配两次,或者接近这个分配。由于不能调整数组的大小,这两种方法都将使用某种机制来收集不断增长的集合中的数据。(好吧,这个列表本身就是一个不断增长的集合。)

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

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


编辑:此答案的最后部分无效。但是,其余的信息仍然是有用的,所以我会留下来。

我知道这是一篇老文章,但在有了同样的问题并做了一些研究之后,我发现了一些有趣的东西,也许值得分享。

首先,我同意@mquander及其答案。他说的是正确的,在表演方面,两者是相同的。

不过,我一直在使用Reflector来查看System.Linq.Enumerable扩展名称空间中的方法,并注意到一个非常常见的优化。尽可能将IEnumerable源投射到IListICollection以优化方法。例如,看看ElementAt(int)

有趣的是,微软选择只优化IList,而不是IList。看起来微软更喜欢使用IList接口。

System.Array只实现IList,因此它不会从这些扩展优化中受益。因此,我认为最佳实践是使用.ToList()方法。如果您使用任何扩展方法,或者将列表传递给另一个方法,那么有可能会针对IList进行优化。


回答得很晚,但我认为这对谷歌有帮助。

当他们使用LINQ创建时都很糟糕。它们都实现了相同的代码,以便在必要时调整缓冲区的大小。ToArray内部使用一个类,通过分配一个由4个元素组成的数组,将IEnumerable<>转换为数组。如果这还不够,则通过创建一个新数组,使当前数组的大小翻倍,并将当前数组复制到该数组。最后,它分配一个新的项目计数数组。如果查询返回129个元素,那么to array将进行6次分配和内存复制操作,以创建一个256个元素数组,而不是另一个129个元素数组。为了提高内存效率。

ToList做了同样的事情,但是它跳过了最后的分配,因为您可以在将来添加项目。列表不关心它是从LINQ查询创建的还是手动创建的。

对于创建列表,内存更好,CPU更差,因为列表是一个通用的解决方案,每个操作都需要在.NET内部数组范围检查之外进行范围检查。

因此,如果您将在结果集中迭代太多次,那么数组是好的,因为它意味着范围检查比列表少,编译器通常会为顺序访问优化数组。

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

为了完成这个答案,我必须写以下句子

  • 最后,您可以使用ToArray或ToList,性能不会有太大的不同(请参见@emp的答案)。
  • 您使用的是C。如果您需要性能,那么不要担心编写高性能代码,而是担心不编写性能差的代码。
  • 始终以X64为目标以获取高性能代码。AXAIK,X64 JIT是基于C++编译器,并做一些有趣的事情,如尾递归优化。
  • 使用4.5,您还可以享受配置文件引导优化和多核JIT。
  • 最后,您可以使用异步/等待模式来更快地处理它。

  • 您应该根据理想的设计选择来决定选择ToListToArray。如果您想要一个只能由索引迭代和访问的集合,请选择ToArray。如果您希望以后在不太麻烦的情况下从集合中添加和删除额外的功能,那么可以执行ToList(实际上您不能添加到数组,但这通常不是正确的工具)。

    如果性能很重要,您还应该考虑什么操作更快。实际上,你不会给ToListToArray打一百万次电话,但可能会给你的收藏品打一百万次。在这方面,[]更好,因为List<>[]有一些开销。有关效率比较,请参阅此线程:哪个效率更高:list或int[]

    不久前在我自己的测试中,我发现ToArray更快。我不知道测试有多偏斜。不过,性能差异非常小,只有在循环中运行这些查询数百万次时,才能注意到这一点。


    我发现人们在这里所做的其他基准都是不足的,所以这就是我在这方面所做的努力。如果你发现我的方法有问题,请告诉我。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    /* 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

    您可以在这里下载linqpad脚本。

    结果:ToArray vs ToList performance

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

  • 当处理较小的数组时,差异就不那么显著了。More iterations, but smaller arrays
  • 在处理ints而不是strings时,差异不那么显著。
  • 使用大的structs而不是strings通常要花更多的时间,但实际上不会改变比率。
  • 这与最高投票答案的结论一致:

  • 除非代码经常生成大量数据列表,否则您不太可能注意到性能差异。(每次创建1000个100K字符串列表时,只有200毫秒的差异。)
  • ToList()一直运行得更快,如果你不打算长时间坚持结果的话,这将是一个更好的选择。
  • 更新

    @Jonhanna指出,根据Select的实施情况,ToList()ToArray()的实施有可能提前预测最终收集的规模。用Where(i => true)替换上面代码中的.Select(i => i),目前的结果非常相似,而且无论.NET实现如何,都更有可能这样做。

    Benchmark using Where instead of Select


    这是一个古老的问题——但是为了让偶然发现它的用户受益,还有一个"memoizing"可枚举的替代方法——它可以缓存和停止一个Linq语句的多个枚举,这就是ToArray()和ToList()经常使用的方法,即使列表或数组的集合属性是NeveR使用。

    memoize可在rx/system.interactive lib中找到,并在此处解释:更多LINQ与System.Interactive

    (摘自Bart de'smet的博客,如果你经常和Linq to Objects打交道,这是一篇强烈推荐的文章)


    一个选项是添加您自己的扩展方法,该方法返回只读ICollection。当您不想使用数组/列表的索引属性或从列表中添加/删除时,这可能比使用ToListToArray要好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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();
        }
    }

    单元测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    [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);
        }
    }


    老问题,新问题的人。

    根据system.linq.enumerable的来源,ToList只返回new List(source),而ToArray使用new Buffer(source).ToArray()返回T[]

    About memory allocation:

    在只运行IEnumerable对象的情况下,ToArray分配内存的时间比ToList多一次。但在大多数情况下,您不必关心它,因为GC将在需要时执行垃圾收集。

    About the runtime efficient:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    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);
    }

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

    Group1:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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]

    Group2:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

    Group3:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

    Group4:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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]

    Group5:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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, 743, 730, 747, 742, 731, 737, 745, 734, 739, 735, 727, 743, 752, 731, 744, 742, 729, 740, 746, 731, 739, 746, 733, 745, 743, 733, 739, 742, 727, 737]

    C1.ToList 31.71 ms -> [35, 32, 32, 30, 31, 33, 31, 32, 32, 31, 31, 32, 32, 33, 32, 31, 31, 32, 31, 32, 32, 32, 31, 32, 33, 32, 31, 31, 31, 32, 31, 34, 31, 31, 32, 33, 32, 32, 31, 32, 34, 32, 31, 32, 33, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 31, 33, 30, 31, 32, 30, 30, 33, 32, 32, 34, 31, 31, 31, 31, 32, 31, 31, 31, 31, 32, 31, 31, 33, 31, 32, 32, 32, 33, 32, 31, 31, 31, 31, 31, 32, 32, 33, 32, 31, 31, 32]
    C1.ToArray 59.53 ms -> [63, 57, 58, 58, 57, 59, 59, 57, 60, 131, 127, 67, 58, 56, 59, 56, 57, 58, 58, 58, 57, 59, 60, 57, 57, 59, 60, 57, 57, 57, 58, 58, 58, 58, 57, 57, 61, 57, 58, 57, 57, 57, 57, 57, 58, 58, 58, 58, 57, 58, 59, 57, 58, 57, 57, 59, 58, 58, 59, 57, 59, 57, 56, 56, 59, 56, 56, 59, 57, 58, 58, 58, 57, 58, 59, 59, 58, 57, 58, 62, 65, 57, 57, 57, 58, 60, 59, 58, 59, 57, 58, 57, 58, 59, 58, 58, 58, 59, 60, 58]
    S1.ToList 82.78 ms -> [87, 82, 83, 83, 82, 82, 83, 84, 82, 83, 84, 84, 84, 82, 82, 84, 82, 84, 83, 84, 82, 82, 82, 81, 83, 83, 83, 84, 84, 82, 82, 83, 83, 83, 82, 83, 85, 83, 82, 82, 84, 82, 82, 83, 83, 83, 82, 82, 82, 83, 82, 83, 82, 84, 82, 83, 82, 83, 82, 82, 82, 84, 82, 83, 82, 82, 86, 83, 83, 82, 83, 83, 83, 82, 84, 82, 83, 81, 82, 82, 82, 82, 83, 83, 83, 82, 83, 84, 83, 82, 83, 83, 83, 82, 83, 84, 82, 82, 83, 83]
    S1.ToArray 122.3 ms -> [122, 119, 119, 120, 119, 120, 120, 121, 119, 119, 122, 120, 120, 120, 122, 120, 123, 120, 120, 120, 121, 123, 120, 120, 120, 121, 120, 121, 122, 120, 123, 119, 121, 118, 121, 120, 120, 120, 119, 124, 119, 121, 119, 120, 120, 120, 120, 120, 122, 121, 123, 230, 203, 123, 119, 119, 122, 119, 120, 120, 120, 122, 120, 121, 120, 121, 120, 121, 120, 121, 120, 120, 120, 121, 122, 121, 123, 119, 119, 119, 119, 121, 120, 120, 120, 122, 121, 122, 119, 120, 120, 121, 121, 120, 121, 120, 121, 118, 118, 118]

    C2.ToList 3.43 ms -> [5, 3, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 6, 4, 4, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3]
    C2.ToArray 3.48 ms -> [3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3]
    S2.ToList 41.47 ms -> [41, 41, 49, 67, 82, 41, 41, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 41, 40, 42, 42, 40, 40, 41, 41, 41, 40, 41, 40, 41, 40, 41, 40, 42, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 41, 41, 41, 42, 40, 41, 40, 40, 40, 42, 40, 41, 42, 41, 42, 41, 42, 40, 41, 41, 41, 41, 41, 41, 41, 41, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 40, 41, 41, 41, 41, 41, 43, 40, 40, 41, 42, 41]
    S2.ToArray 40.62 ms -> [42, 41, 44, 40, 40, 40, 40, 41, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 41, 40, 41, 40, 40, 41, 42, 41, 41, 41, 40, 40, 40, 40, 40, 41, 41, 42, 40, 41, 41, 41, 41, 41, 40, 42, 40, 40, 41, 41, 41, 40, 41, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 40, 41, 42, 40, 41, 41, 42, 41, 41, 40, 41, 40, 41, 40, 41, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40]

    由于stackoverflow限制了答案的字符数,因此省略了group2和group3的示例列表。

    如您所见,在大多数情况下使用ToListToArry并不重要。

    在处理运行时计算的IEnumerable对象时,如果计算带来的负载比ToListToArray的内存分配和复制操作重,则差异不显著(C.ToList vs C.ToArrayS.ToList vs S.ToArray

    这种差异只能在非运行时计算的IEnumerable对象(C1.ToList vs C1.ToArrayS1.ToList vs S1.ToArray上观察到。但100万个小物体的绝对差(<60毫秒)仍然可以接受。实际上,差异是由IEnumerableEnumerator的实施决定的。所以,如果你的程序真的对这个非常敏感,你就必须进行分析,分析,分析!最后,您可能会发现瓶颈不在ToListToArray上,而在枚举器的细节上。

    并且,C2.ToList vs C2.ToArrayS2.ToList vs S2.ToArray的结果表明,您确实不需要在非运行时计算的ICollection对象上关心ToListToArray

    当然,这只是我的机器上的结果,这些操作在不同的机器上花费的实际时间是不一样的,你可以在你的机器上找到上面使用的代码。

    您需要做出选择的唯一原因是,您对ListT[]有特定的需求,如@jeppe stig nielsen的回答所述。


    最好是ToListAsync()

    在实体框架6中,两个方法最终都调用同一个内部方法,但ToArrayAsync()在最后调用list.ToArray(),它实现为

    1
    2
    3
    T[] array = new T[_size];
    Array.Copy(_items, 0, array, 0, _size);
    return array;

    因此,ToArrayAsync()有一些日常开支,因此,ToListAsync()是首选。


    对于有兴趣在另一个Linq to SQL中使用此结果的任何人,例如

    1
    2
    3
    from q in context.MyTable
    where myListOrArray.Contains(q.someID)
    select q;

    然后生成的SQL与您使用的MyListorArray列表或数组相同。现在我知道有些人可能会问为什么在这个语句之前还要枚举,但是从iqueryable vs(列表或数组)生成的SQL之间有区别。