我的代码中有一些通用列表,其中包含数十或数百个元素。Clear()
有时我需要用其他对象重新填充这个列表,所以问题是:调用方法或创建一个更快的方法是new List<T>()
什么?
8 回答
什么会更快,调用
Clear()
方法或创建一个`new List()?
这是不可能回答的。这真的取决于很多因素,包括收藏品存在多长时间。
这里最好的选择是:
分析应用程序,看看这是否真的很重要。它可能不会产生任何明显的差异,在这种情况下,我会使用对您如何看待这个对象最有意义的方法。
如果确实重要,请编写两组代码,并测量速度差异(如果有)。
从实际的角度来看,调用Clear()
实际上不会减少内存(由List<T>
自身使用),因为它不会缩小列表的容量,只会消除其中包含的值。创建一个List<T>
新列表将导致分配一个新列表,这反过来又会导致更多的分配增长。
然而,这并不意味着它会变慢 - 在许多情况下,重新分配会更快,因为您不太可能将大型数组提升到更高的垃圾收集世代,这反过来可以使 GC 过程更快。
如果不知道您的确切场景并在分析器中进行测量,则无法知道您的场景中哪个更好。
我已经运行了这个测试:
private static void Main(string[] args)
{
int defaultN = 1000;
Stopwatch sw = new Stopwatch();
while (true)
{
Console.WriteLine("Enter test elements number:");
int n;
if (!int.TryParse(Console.ReadLine(), out n)) n = defaultN;
else defaultN = n;
Console.WriteLine($"Test with {n} elements");
List<object> list = Enumerable.Repeat(new object(), n).ToList();
sw.Start();
Clear(list);
sw.Stop();
Console.WriteLine("Clear: {0} ms", sw.ElapsedTicks / 10000D);
GC.Collect();
GC.WaitForPendingFinalizers();
List<object> list2 = Enumerable.Repeat(new object(), n).ToList();
sw.Restart();
Reinitialize(list2);
sw.Stop();
Console.WriteLine("Reinitialize: {0} ms", sw.ElapsedTicks / 10000D);
GC.Collect();
GC.WaitForPendingFinalizers();
List<object> list3 = Enumerable.Repeat(new object(), n).ToList();
sw.Restart();
ReinitializeAndCollect(list3);
sw.Stop();
Console.WriteLine("ReinitializeAndCollect: {0} ms", sw.ElapsedTicks / 10000D);
Console.WriteLine("===");
}
}
private static List<object> Clear(List<object> list)
{
list.Clear();
return list;
}
private static List<object> Reinitialize(List<object> list) => new List<object>();
private static List<object> ReinitializeAndCollect(List<object> list)
{
list = new List<object>();
GC.Collect();
GC.WaitForPendingFinalizers();
return list;
}
我的结论基于我的普通核心 i3 处理器的结果:
如果有数千个元素 - 最好清除列表。它速度快,内存效率高。
如果集合有超过 100 000 个元素 - 重新初始化变得更有吸引力。如果在分析之后您认为这里存在瓶颈,请使用它。重新初始化会非常快,但正如第三种方法测试所示,未来的垃圾收集将与清除列表一样慢。
如此简短的回答是:如果您没有分析您的应用程序,请使用Clear
. 重用对象是好的。如果你这样做了——你已经知道该怎么做了。
这将取决于很多因素,从长远来看,它在您的程序中可能并不重要(足以计算在内)。
来自 msdn文档 .Clear()
的是 O(n) 操作。
初始化一个新实例将有它自己的开销以及(如果你保持集合相同的长度,一个 O(n) 操作:即 nAdd()
次调用)。
真正测试这一点的唯一方法是在你的程序中设置一些秒表,看看如果你真的认为它值得,效果是什么。在所有的可能性; 这不值得。
我的想法是,如果您已经创建了一个集合,Clear()
那么这就是为什么Clear()
首先要有一种方法。
虽然这可能令人沮丧,但答案确实是无关紧要。两者之间的时间差异将非常小,以至于它可能不会对您的应用程序产生任何影响。做可以使代码更清晰、更易于理解的事情,并尽量不要为微优化而编程。
Clear()
将删除所有元素并保持现有容量,而创建新 List 将需要从托管堆中至少分配一次(如果初始容量很小,可能会添加更多项)。
如果您有大量项目,并且每次迭代的项目数量大致相同,那么使用
Clear
可能会稍微快一些。如果您在一次迭代中有非常多的项目,那么在后续迭代中数量会少得多,那么使用
Clear
成本可能会更高,因为您将在内存中保留一个具有不必要大容量的列表。
当然,在许多(大多数?)场景中,差异可以忽略不计。
也许我在这里做了一些根本错误的事情,但是在使用 C# 开发 ASP.NET 应用程序时,我在使用 Clear() 与 new 时遇到了很大的不同。我正在创建一个带有图表的统计页面,其中包含数据系列。对于每个图表,我都有一个部分执行此操作:
chart = new ChartistChart() { Title = "My fancy chart" };
series = new List<ChartistMetaValue>();
*some code for getting the statistics*
chart.Series.Add(series);
chartistLineCharts.Add(chart);
然后是另一个图表。
chart = new ChartistChart() { Title = "My second fancy chart" };
series = new List<ChartistMetaValue>();
*some code for getting the statistics*
chart.Series.Add(series);
chartistLineCharts.Add(chart);
这适用于series
重新分配new
,但是当我这样做时
series.Clear();
相反,我实际上清除了里面的条目chart.Series
,chartistLineCharts
因此统计页面最终只检索最后一个图表的系列。我假设这里有一些链接,比如内存指针,这与最初讨论的问题不同,但这至少是选择new
. Clear()
也许有一种方法可以避免它。
我为自己做了几次测试。结果(速度)是:
- 对于小列表 - 例如 3 个项目,创建新列表的速度更快,但差异不大
- 对于平均 10 个或更多项目,最好清除列表。对于价值类型要好得多(例如 3-4 倍),对于价值时间要好 20%。
但最终,最好对应用进行剖析,找出整个应用的瓶颈。
如果您的对象是值类型,我会使用 Clear() 来减少未来的内存分配。否则,两种方法几乎相同。