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

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

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() -安全的知识,内存不会被分配两次?

126435 次浏览

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

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

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

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

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

如果在IEnumerable<T>上使用ToList()(例如,来自ORM),则通常首选它。如果序列的长度在开始时不知道,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中可用。交互式库,并在这里解释: # EYZ0 < / p >

(从Bart De 's met的博客开始,如果你经常使用Linq to Objects,建议阅读高度)

一种选择是添加自己的扩展方法,返回只读的 ICollection<T>。当您既不想使用数组/列表的索引属性,也不想从列表中添加/删除时,这比使用ToListToArray要好。

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(并不是说您不能添加到数组中,但这通常不是合适的工具)。

如果性能很重要,您还应该考虑哪些操作会更快。实际上,你不会调用ToListToArray一百万次,但是可能在获得的集合上工作一百万次。在这方面[]更好,因为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生成的SQL与(List或Array)之间是有区别的

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

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

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

编辑

有几个人问我,在List<T>值中有多余的未使用内存会带来什么后果。

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

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

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

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

当他们使用linq创建时,他们都很糟糕。它们都实现了相同的代码必要时调整缓冲区大小ToArray内部使用一个类将IEnumerable<>转换为数组,通过分配一个包含4个元素的数组。如果这还不够,它会创建一个新数组,将当前数组的大小翻倍,并将当前数组复制到它。在结束时,它分配一个新的数组的计数你的项目。如果你的查询返回129个元素,那么ToArray将进行6次分配和内存复制操作,创建一个256个元素的数组,然后am另一个129个元素的数组返回。关于记忆效率就说这么多。

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

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

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

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

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

  1. 最后,你可以使用ToArray或ToList,性能不会有太大的不同(见@EMP的答案)。
  2. 您正在使用c#。如果您需要性能,那么不要担心编写高性能代码,而是担心不编写性能差的代码。
  3. 始终将x64作为高性能代码的目标。x64 JIT基于c++编译器,做了一些有趣的事情,比如尾部递归优化。
  4. 在4.5中,您还可以享受配置文件引导的优化和多核JIT。
  5. 最后,您可以使用async/await模式来更快地处理它。

(七年后……)

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

这篇文章只是补充提到了<强> < / >强语义差异,它存在于由数组(T[])生成的IEnumerator<T>和由数组(# EYZ2)返回的<强> < / >强语义差异之间。

最好用例子来说明:

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>没有跟踪自创建枚举器以来数组是否被修改过。

注意,我将局部变量source声明为IList<int>。通过这种方式,我确保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

接着是System.InvalidOperationException的留言:

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

本例中的底层枚举数是公共可变值类型System.Collections.Generic.List`1+Enumerator[System.Int32](在本例中被框在IEnumerator<int>框内,因为我使用IList<int>)。

结论<强劲>,< / >强List<T>生成的枚举器跟踪列表在枚举过程中是否发生变化,而T[]生成的枚举器则不会。所以在选择.ToList().ToArray()时要考虑这个差异。

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

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

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

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

关于内存分配:

当在# EYZ0只对象上运行时,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的示例列表。

正如您所看到的,在大多数情况下使用ToListToArry并不重要。

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

只有在非运行时计算的IEnumerable<T>对象(C1.ToList vs C1.ToArrayS1.ToList vs S1.ToArray)上才能观察到这种差异。但是对于一个100万的小对象IEnumerable<T>来说,绝对差异(<60ms)仍然是可以接受的。事实上,差异是由IEnumerable<T>Enumerator<T>的实现决定的。所以,如果你的程序对这个非常非常非常敏感,你必须使用简介,简介,简介!最后,您可能会发现瓶颈不是在ToListToArray上,而是枚举器的细节。

而且,C2.ToList vs C2.ToArrayS2.ToList vs S2.ToArray的结果表明,您真的不需要关心非运行时计算的ICollection<T>对象上的ToListToArray

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

您需要做出选择的唯一原因是,您对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脚本

< p >结果: # EYZ0 < / p >

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

  1. 处理较小的数组. #时,差异不太显著。 李# EYZ0 < / >
  2. 当处理# eyz0而不是# eyz1时,差异不太显著。
  3. 使用大的# eyz0而不是# eyz1通常会花费更多的时间,但并没有真正改变太多的比率。

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

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

更新

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

Benchmark using Where instead of Select

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)