78

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

4

8 回答 8

66

什么会更快,调用Clear()方法或创建一个`new List()?

这是不可能回答的。这真的取决于很多因素,包括收藏品存在多长时间。

这里最好的选择是:

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

  2. 如果确实重要,请编写两组代码,并测量速度差异(如果有)。

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

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

如果不知道您的确切场景并在分析器中进行测量,则无法知道您的场景中哪个更好。

于 2012-06-05T16:26:02.737 回答
30

我已经运行了这个测试:

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. 重用对象是好的。如果你这样做了——你已经知道该怎么做了。

于 2013-06-06T08:39:33.137 回答
8

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

来自 msdn文档 .Clear()的是 O(n) 操作。

初始化一个新实例将有它自己的开销以及(如果你保持集合相同的长度,一个 O(n) 操作:即 nAdd()次调用)。

真正测试这一点的唯一方法是在你的程序中设置一些秒表,看看如果你真的认为它值得,效果是什么。在所有的可能性; 这不值得。

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

于 2012-06-05T16:31:27.347 回答
5

虽然这可能令人沮丧,但答案确实是无关紧要。两者之间的时间差异将非常小,以至于它可能不会对您的应用程序产生任何影响。做可以使代码更清晰、更易于理解的事情,并尽量不要为微优化而编程。

于 2012-06-05T16:24:07.433 回答
4

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

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

  • 如果您在一次迭代中有非常多的项目,那么在后续迭代中数量会少得多,那么使用Clear成本可能会更高,因为您将在内存中保留一个具有不必要大容量的列表。

当然,在许多(大多数?)场景中,差异可以忽略不计。

于 2012-06-05T16:37:08.187 回答
4

也许我在这里做了一些根本错误的事情,但是在使用 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.SerieschartistLineCharts因此统计页面最终只检索最后一个图表的系列。我假设这里有一些链接,比如内存指针,这与最初讨论的问题不同,但这至少是选择new. Clear()也许有一种方法可以避免它。

于 2016-03-07T16:16:03.760 回答
4

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

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

但最终,最好对应用进行剖析,找出整个应用的瓶颈。

于 2016-05-02T08:20:09.610 回答
1

如果您的对象是值类型,我会使用 Clear() 来减少未来的内存分配。否则,两种方法几乎相同。

于 2012-06-05T16:25:57.090 回答