Подтвердить что ты не робот

Что быстрее: очистить коллекцию или создать новый

У меня есть несколько общих списков в моем коде, которые имеют десятки или сотни элементов. Иногда мне нужно пополнить эти списки другими объектами, поэтому вопрос: что будет быстрее, вызвать метод Clear() или создать new List<T>()?

4b9b3361

Ответ 1

что будет быстрее, вызвать метод Clear() или создать новый List()?

Это невозможно ответить. Это действительно зависит от множества факторов, включая продолжительность существования коллекции.

Лучший вариант здесь:

  • Профилируйте приложение и посмотрите, действительно ли это имеет значение. Вероятно, это не приведет к какой-либо заметной разнице, и в этом случае я бы использовал метод, который имеет наибольший смысл с точки зрения того, как вы думаете об этом объекте.

  • Если это имеет значение, напишите оба набора кода и измерьте разницу в скорости (если есть).

С практической точки зрения вызов Clear() фактически не уменьшит память (используется самим List<T>), так как он не уменьшает емкость списка, а только исключает значения, содержащиеся в нем. Создание нового List<T> приведет к назначению нового списка, что, в свою очередь, приведет к увеличению количества ресурсов с ростом. Это, однако, не означает, что он будет медленнее - во многих случаях перераспределение будет быстрее, поскольку вы менее склонны продвигать большие массивы в более высокие поколения мусора, что, в свою очередь, может ускорить процесс GC. Не зная своего точного сценария и измерения в профилировщике, нет способа узнать, что лучше в вашем сценарии.

Ответ 2

Я проверил этот тест:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace TestSpeed
{
    public class Program
    {
        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 {0} elements", n);

                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("===");
            }
        }

        static List<object> Clear(List<object> list)
        {
            list.Clear();
            return list;
        }

        static List<object> Reinitialize(List<object> list)
        {
            return new List<object>();
        }

        static List<object> ReinitializeAndCollect(List<object> list)
        {
            list = new List<object>();

            GC.Collect();
            GC.WaitForPendingFinalizers();

            return list;
        }
    }
}

Мой вывод основан на результатах моего обычного ядра i3-процессора:

В случае тысяч элементов - лучше очистить список. Это быстро и эффективно.

Если коллекция насчитывает более 100 000 элементов - повторная инициализация становится более привлекательной. Если после профилирования вы думаете, что здесь есть узкое место, используйте его. Повторная инициализация будет очень быстрой, но, как показывает третий метод, будущий сбор мусора будет примерно таким же медленным, как просто очистка списка.

Так короткий ответ: если вы не профилировали свое приложение, используйте Clear. Повторное использование объектов - это хорошо. Если вы это сделали - вы уже знаете, что делать.

Ответ 3

Это будет зависеть от множества факторов, и в конечном итоге это, вероятно, не будет иметь значения (достаточно для подсчета) в вашей программе.

Из msdn docs .Clear() - это операция O (n).

Инициализация нового экземпляра будет иметь свои собственные служебные данные, а также (если вы сохраните коллекцию в той же самой длине, операцию O (n): i) n Add()).

Действительно, единственный способ проверить это - настроить некоторые секундомеры в вашей программе и посмотреть, что это за эффект, если вы действительно думаете, что это того стоит. По всей вероятности; это не стоит.

Мои мысли состоят в том, что если вы уже создали коллекцию, Clear(), то почему существует метод Clear(), в первую очередь.

Ответ 4

Хотя это может быть неприятно, ответ на самом деле заключается в том, что это не имеет значения. Разница во времени между ними будет настолько малой, что, вероятно, это не повлияет на ваше приложение. Сделайте то, что приводит к более чистым, понятным кодам и старайтесь не программировать для микро-оптимизации.

Ответ 5

Clear() удалит все элементы и сохранит существующую емкость, тогда как для создания нового списка потребуется по крайней мере одно выделение из управляемой кучи (возможно, больше по мере добавления элементов, если начальная емкость мала).

  • Если у вас есть большое количество элементов, а количество элементов примерно одинаково на каждой итерации, то использование Clear возможно немного быстрее.

  • Если у вас есть исключительно большое количество элементов на одной итерации, то гораздо меньшее число при последующих итерациях, а затем с использованием Clear потенциально более дорогостоящее, потому что вы будете хранить в памяти список с излишне большая емкость.

Конечно, во многих (наиболее?) сценариях разница будет незначительной.

Ответ 6

Возможно, я делаю что-то принципиально неправильное здесь, но при разработке приложения ASP.NET на С# я сталкиваюсь с большой разницей при использовании Clear() vs. 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 over Clear(). Возможно, есть способ избежать этого.

Ответ 7

Я сделал несколько тестов для себя. Результаты (скорость):

  • для небольших списков - например, 3 элемента, быстрее создавать новые списки, но разница невелика.
  • для 10 или более пунктов в среднем лучше очищать списки. Для типов значений намного лучше (например, 3-4 раза), для значений, например, на 20% лучше.

Но в итоге лучше профилировать приложение и найти узкие места для всего приложения.

Ответ 8

Если ваши объекты являются типами значений, я бы использовал Clear() для уменьшения будущих распределений памяти. В противном случае оба подхода почти идентичны.