我经常遇到这样的情况:我想在声明查询的地方对查询进行求值。这通常是因为我需要对它进行多次迭代,计算成本很高。例如:

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

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

然而,我想知道ToArray()是否通过首先调用ToList()来实现,因此内存效率比只调用ToList()低。

我疯了吗?我是否应该调用ToArray() -在知道内存不会被分配两次的情况下安全可靠?


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

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


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

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

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


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


我知道这是一个老帖子,但在有了同样的问题和做了一些研究之后,我发现了一些有趣的东西,可能值得分享。

首先,我同意@mquander和他的回答。在性能方面,两者是相同的。

但是,我一直在使用Reflector查看System.Linq.Enumerable扩展名称空间中的方法,并注意到一个非常常见的优化。 只要可能,IEnumerable<T>源就转换为IList<T>或ICollection<T>来优化方法。例如,查看ElementAt(int)。

有趣的是,微软选择只优化IList<T>,而不是IList。微软似乎更喜欢使用IList<T>接口。


我同意@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

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

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


这是一个老问题了——但是为了方便无意中发现它的用户,还有一种“Memoizing”Enumerable的替代方案——它具有缓存和停止Linq语句的多个枚举的效果,这就是ToArray()和ToList()经常被使用的原因,即使列表或数组的集合属性从未被使用。

Memoize在RX/System中可用。交互式库,并在这里解释: 更多LINQ与系统。互动

(摘自Bart De 's met的博客,如果你经常使用Linq to Objects,强烈推荐你阅读)


一种选择是添加自己的扩展方法,该方法返回一个只读的ICollection<T>。当您既不想使用数组/列表的索引属性,也不想从列表中添加/删除时,这可能比使用ToList或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();
    }
}

单元测试:

[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);
    }
}

您应该根据理想的设计选择来决定使用ToList还是ToArray。如果您想要一个只能通过索引迭代和访问的集合,请选择ToArray。如果您希望以后能够轻松地从集合中添加和删除额外的功能,那么可以使用ToList(并不是说您不能添加到数组中,但这通常不是合适的工具)。

如果性能很重要,您还应该考虑哪些操作会更快。实际上,您不会调用ToList或ToArray一百万次,但可能会对获得的集合进行一百万次操作。在这方面[]更好,因为List<>是[],有一些开销。查看这个线程的一些效率比较:List<int>或int[]

在我自己不久前的测试中,我发现ToArray更快。我不确定这些测试有多偏颇。然而,性能差异是如此微不足道,只有在循环运行这些查询数百万次时才能明显看出。


对于任何有兴趣在其他Linq-to-sql中使用此结果的人,例如

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

那么生成的SQL是相同的,无论你使用List或Array为myListOrArray。 现在我知道有些人可能会问为什么在这条语句之前枚举,但从IQueryable vs(列表或数组)生成的SQL之间是有区别的。


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

两者都使用数组进行存储,但是ToList有一个更灵活的约束。它需要数组至少与集合中的元素数量一样大。如果数组更大,这不是问题。但是ToArray需要数组的大小精确到元素的数量。

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

EDIT

有几个人问我在List<T>值中有额外的未使用内存的后果。

这是一个合理的担忧。如果创建的集合寿命很长,在创建后从未被修改过,并且有很高的机会落在Gen2堆中,那么您可能会更好地预先分配额外的ToArray。

总的来说,我发现这种情况比较罕见。更常见的情况是,大量ToArray调用被立即传递给其他短期内存使用,在这种情况下,ToList显然更好。

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


一个很晚的答案,但我认为这对谷歌人有帮助。

They both suck when they created using linq. They both implement same code to resize buffer if necessary. ToArray internally uses a class to convert IEnumerable<> to array, by allocating an array of 4 elements. If that is not enough than it doubles the size by creating a new array double the size of current and copying current array to it. At the end it allocates a new array of count of your items. If your query returns 129 elements then ToArray will make 6 allocations and memory copy operations to create a 256 element array and than am another array of 129 to return. so much for memory efficiency.

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

List在内存上更好,但在cpu上更差,因为List是一个通用的解决方案,每个操作都需要范围检查,除了.net内部的数组范围检查之外。

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

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

为了完成这个问题,我必须写出下面的句子

At the end, you can use either an ToArray, or ToList, performance will not be so different ( see answer of @EMP ). You are using C#. If you need performance then do not worry about writing about high performance code, but worry about not writing bad performance code. Always target x64 for high performance code. AFAIK, x64 JIT is based on C++ compiler, and does some funny things like tail recursion optimizations. With 4.5 you can also enjoy the profile guided optimization and multi core JIT. At last, you can use async/await pattern to process it quicker.


(七年后……)

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

这篇文章只是一个补充,以提及由数组(T[])产生的IEnumerator<T>与由List<T>返回的IEnumerator之间存在的语义差异。

最好用例子来说明:

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
900
10

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

Note that I declared the local variable source as an IList<int>. In that way I make sure the C# compiler does not optimze the foreach statement into something which is equivalent to a for (var idx = 0; idx < source.Length; idx++) { /* ... */ } loop. This is something the C# compiler might do if I use var source = ...; instead. In my current version of the .NET framework the actual enumerator used here is a non-public reference-type System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32] but of course this is an implementation detail.

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

1
2
3
4
5

其次是一个系统。InvalidOperationException爆炸说:

修改集合;枚举操作可能无法执行。

在这种情况下,底层枚举器是公共可变值类型System. collections . generic . list ' 1+ enumerator [System. collections . generic . list]。Int32](在这种情况下,在IEnumerator<int>框内,因为我使用IList<int>)。

综上所述,List<T>生成的枚举数跟踪列表在枚举过程中是否发生变化,而T[]生成的枚举数则没有。因此,在. tolist()和. toarray()之间进行选择时,请考虑此差异。

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

(如果有人想知道List<>如何跟踪集合是否被修改,这个类中有一个私有字段_version,每当List<>被更新时,它都会被更改。实际上可以通过简单地删除索引器public T this[int index]的set访问器中增加_version的行来改变List<>的这种行为,就像最近在Dictionary<,>中所做的那样,如另一个答案所述。)


老问题,但总是有新的提问者。

根据来源的System.Linq。可枚举,ToList只返回一个新的List(source),而ToArray使用一个新的Buffer<T>(source).ToArray()返回一个T[]。

关于内存分配:

当运行在IEnumerable<T> only对象上时,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);
}

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

Group1:

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上:

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:

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:

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:

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的示例列表。

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

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

The difference can be observed only on non-runtime-calculated IEnumerable<T> only objects (C1.ToList vs C1.ToArray and S1.ToList vs S1.ToArray). But the absolute difference (<60ms) is still acceptable on an one million small object IEnumerable<T>. In fact, the difference is decided by the implementation of Enumerator<T> of IEnumerable<T>. So, if your program is really really really sensitive on this, you do have to profile, profile, profile! At last you'll probably find that the bottleneck is not on ToList or ToArray, but the detail of enumerators.

C2的结果。ToList vs C2。ToArray和S2。ToList vs S2。ToArray表明,您真的不需要关心非运行时计算的ICollection<T>对象上的ToList或ToArray。

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

你需要做出选择的唯一原因是,你在List<T>或T[]上有特定的需求,正如@Jeppe Stig Nielsen的回答所描述的那样。


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

/* 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脚本。

结果:

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

当处理较小的数组时,差异就不那么显著了。 在处理整型而不是字符串时,这种差异不太显著。 使用大型结构体而不是字符串通常会花费更多的时间,但并不会真正改变比例。

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

除非您的代码经常生成许多大型数据列表,否则不太可能注意到性能上的差异。(当创建1000个包含100K字符串的列表时,只有200ms的差异。) ToList()始终运行得更快,如果不打算长时间保留结果,那么它是一个更好的选择。

更新

@JonHanna指出,根据Select的实现,ToList()或ToArray()实现可以提前预测结果集合的大小。将上面代码中的. select (i => i)替换为Where(i => true)会产生非常相似的结果,并且更有可能这样做,而不管. net实现如何。


首选ToListAsync<T>()。

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

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

所以ToArrayAsync<T>()有一些开销,因此ToListAsync<T>()是首选。


编辑2:(这是对原始答案的更正)

使用基准。NET,我们可以通过性能测量来确认,公认的答案实际上是正确的:ToList在一般情况下更快,因为它不需要从已分配的缓冲区中修剪空空间。ToArray可能会执行额外的分配和复制操作,以使缓冲区的大小精确到元素的数量。

为了确认这一点,使用下面的基准测试。

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

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

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

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

结果证实,在大多数情况下,ToList要快10% - 15%。

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
Intel Core i9-10885H CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.302
  [Host]     : .NET 6.0.7 (6.0.722.32202), X64 RyuJIT
  DefaultJob : .NET 6.0.7 (6.0.722.32202), X64 RyuJIT


|  Method | Count |         Mean |      Error |     StdDev | Ratio | RatioSD |   Gen 0 |  Gen 1 | Allocated |
|-------- |------ |-------------:|-----------:|-----------:|------:|--------:|--------:|-------:|----------:|
| ToArray |     0 |     29.73 ns |   0.546 ns |   0.536 ns |  1.00 |    0.00 |  0.0067 |      - |      56 B |
|  ToList |     0 |     31.51 ns |   0.485 ns |   0.405 ns |  1.06 |    0.02 |  0.0105 |      - |      88 B |
|         |       |              |            |            |       |         |         |        |           |
| ToArray |     1 |     37.36 ns |   0.314 ns |   0.294 ns |  1.00 |    0.00 |  0.0114 |      - |      96 B |
|  ToList |     1 |     36.75 ns |   0.605 ns |   0.537 ns |  0.98 |    0.01 |  0.0153 |      - |     128 B |
|         |       |              |            |            |       |         |         |        |           |
| ToArray |     6 |    100.05 ns |   1.522 ns |   1.349 ns |  1.00 |    0.00 |  0.0286 |      - |     240 B |
|  ToList |     6 |     85.16 ns |   0.808 ns |   0.756 ns |  0.85 |    0.01 |  0.0267 |      - |     224 B |
|         |       |              |            |            |       |         |         |        |           |
| ToArray |    10 |    137.20 ns |   2.766 ns |   2.452 ns |  1.00 |    0.00 |  0.0372 |      - |     312 B |
|  ToList |    10 |    123.05 ns |   2.198 ns |   1.949 ns |  0.90 |    0.01 |  0.0372 |      - |     312 B |
|         |       |              |            |            |       |         |         |        |           |
| ToArray |    42 |    398.25 ns |   6.583 ns |   5.836 ns |  1.00 |    0.00 |  0.0877 |      - |     736 B |
|  ToList |    42 |    352.04 ns |   4.976 ns |   4.411 ns |  0.88 |    0.02 |  0.0887 |      - |     744 B |
|         |       |              |            |            |       |         |         |        |           |
| ToArray |   100 |    730.80 ns |   6.501 ns |   6.081 ns |  1.00 |    0.00 |  0.1488 |      - |   1,248 B |
|  ToList |   100 |    705.49 ns |   9.947 ns |   9.305 ns |  0.97 |    0.01 |  0.1526 |      - |   1,280 B |
|         |       |              |            |            |       |         |         |        |           |
| ToArray |  1337 |  8,023.57 ns | 147.388 ns | 137.867 ns |  1.00 |    0.00 |  1.6785 | 0.0458 |  14,056 B |
|  ToList |  1337 |  7,980.27 ns | 138.469 ns | 122.749 ns |  1.00 |    0.02 |  1.9989 | 0.1221 |  16,736 B |
|         |       |              |            |            |       |         |         |        |           |
| ToArray | 10000 | 57,037.19 ns | 510.492 ns | 452.538 ns |  1.00 |    0.00 | 12.6343 | 1.7700 | 106,280 B |
|  ToList | 10000 | 57,728.15 ns | 583.353 ns | 517.127 ns |  1.01 |    0.01 | 15.5640 | 5.1270 | 131,496 B |

作为参考,下面是原始答案,不幸的是,它只在一个非常特殊的情况下执行基准测试,避免了中间的调整大小和复制操作。

最初的回答:

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

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

编辑:从注释中可以看出,这些基准测试可能不是指示性的,因为Enumerable.Range(…)返回一个IEnumerable<T>,其中包含关于序列大小的信息,随后在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>();
    }

结果如下:


    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)