哪个更快: 清除收集或实例化 new

在我的代码中有一些泛型列表,它们有几十个或几百个元素。 有时我需要用其他对象重新填充这个列表,所以问题是: 调用 Clear()方法或创建 new List<T>()哪个更快?

44221 次浏览

虽然这可能令人沮丧,但答案是这不应该是重要的。两者之间的时间差异非常小,可能不会对您的应用程序产生任何影响。做什么导致更清晰,更易懂的代码,并尝试不要为微观优化编程。

如果对象是值类型,我会使用 Clear ()来减少内存 未来分配。

调用 Clear()方法或者创建一个‘ new List ()’哪个更快?

这个问题无法回答。这确实取决于很多因素,包括收藏品存在的时间。

最好的选择是:

  1. 分析应用程序,看看这是否真的重要。它可能不会产生任何可感知的差异,在这种情况下,我会使用最有意义的方法来考虑如何看待这个对象。

  2. 如果有必要,编写两组代码,并测量速度上的差异(如果有的话)。

从实际的角度来看,调用 Clear()实际上不会减少内存(由 List<T>本身使用) ,因为它不会缩小列表的容量,只会消除其中包含的值。创建一个新的 List<T>将导致分配一个新的列表,这又将导致更多的分配随着增长而增加。

然而,这并不意味着它会变慢——在许多情况下,重新分配将是 再快点,因为您不太可能将大型数组提升到更高的垃圾收集代数,这反过来又可以保持 GC 进程更快。

如果不知道分析器中的确切场景和 测量,就没有办法知道哪个场景更好。

这将取决于许多因素,从长远来看,它在您的程序中可能并不重要(足以计数)。

从 msdn 医生 .Clear()是一个 O (n)操作。

初始化一个新实例将有自己的开销,以及(如果保持集合的长度不变,则执行 O (n)操作: 即 n 个 Add()调用)。

真正测试这个的唯一方法是在程序中设置一些秒表,看看是否值得。十有八九,这不值得。

我的想法是,如果您已经创建了一个集合,那么它就是 Clear(),这就是为什么首先要有一个 Clear()方法的原因。

Clear()将删除所有元素,并维护现有容量,而创建一个新 List 将需要从托管堆中至少进行一次分配(如果初始容量较小,则可能需要添加更多项)。

  • 如果您有大量的项目,并且每次迭代中的项目数大致相同,那么使用 Clear可能会稍微快一些。

  • 如果在一次迭代中有特别多的项,那么在后续迭代中就会有更少的项,那么使用 Clear的代价可能会更高,因为您将在内存中保留一个容量不必要地大的列表。

当然,在许多(大多数?)情况下,这种差异可以忽略不计。

我做了个测试:

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处理器的结果得出的:

对于成千上万的元素-最好是清除列表。它是快速和高效的内存。

如果集合有超过100000个元素-重新初始化将变得更有吸引力。如果在分析之后您认为这里存在瓶颈,那么使用它。重新初始化将非常快,但正如第三个方法测试所显示的,未来的垃圾收集将与清除列表一样慢。

所以简短的回答是: 如果您没有分析您的应用程序,使用 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);

seriesnew一起重新分配时,这种方法工作得很好,但是当我这样做时

series.Clear();

相反,我实际上清除了 chart.SerieschartistLineCharts中的条目,所以统计页面最终只检索最后一个图表的序列。我假设这里有一些链接,比如一个内存指针,这与最初讨论的问题不同,但是这至少是选择 new而不是 Clear()的一个原因。也许有办法可以避免。

我为自己做了几个测试,结果(速度)是:

  • 对于小列表-例如3个项目,创建新列表更快,但差别不大
  • 对于平均10个或更多的项目,最好清除列表。对于值类型要好得多(例如3-4倍) ,对于值类型要好20% 。

但最终,还是应该对应用程序进行剖析,找出整个应用程序的瓶颈。