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

С# List <T>. Производительность Toorray плоха?

Я использую .Net 3.5 (С#), и я слышал, что производительность С# List<T>.ToArray является "плохим", так как память копирует все элементы для формирования нового массива. Это правда?

4b9b3361

Ответ 1

Нет, это не так. Производительность хороша, так как все, что она делает, это копирование памяти всеми элементами (*) для формирования нового массива.

Конечно, это зависит от того, что вы определяете как "хорошую" или "плохую" производительность.

(*) ссылки для ссылочных типов, значения для типов значений.

ИЗМЕНИТЬ

В ответ на ваш комментарий использование Reflector - хороший способ проверить реализацию (см. ниже). Или просто подумайте несколько минут о том, как вы его реализуете, и полагайтесь на то, что инженеры Microsoft не придумают худшего решения.

public T[] ToArray()
{
    T[] destinationArray = new T[this._size];
    Array.Copy(this._items, 0, destinationArray, 0, this._size);
    return destinationArray;
}

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

РЕДАКТИРОВАТЬ 2

В ответ на комментарий: "Нет перестройки объектов?"

Никакой реконструкции для ссылочных типов. Для типов значений значения копируются, что можно легко описать как реконструкцию.

Ответ 2

Причины вызова ToArray()

  • Если возвращаемое значение не предназначено для изменения, его возврат в виде массива делает этот факт более понятным.
  • Если вызывающий абонент, как ожидается, выполнит много не последовательных доступов к данным, может быть преимущество производительности для массива над List < > .
  • Если вы знаете, что вам нужно передать возвращаемое значение сторонней функции, которая ожидает массив.
  • Совместимость с вызывающими функциями, которые должны работать с .NET версии 1 или 1.1. Эти версии не имеют типа List < > (или любых общих типов, если на то пошло).

Причины не вызывать ToArray()

  • Если вызывающему абоненту когда-либо нужно добавлять или удалять элементы, обязательно требуется список < > .
  • Преимущества производительности не обязательно гарантированы, особенно если вызывающий абонент получает доступ к данным последовательным образом. Существует также дополнительный шаг преобразования из списка < > в массив, который требует времени обработки.
  • Вызывающий может всегда преобразовывать список в массив.

взято из здесь

Ответ 3

Да, это правда, что это копия памяти всех элементов. Это проблема производительности? Это зависит от ваших требований к производительности.

A List содержит массив внутри, чтобы удерживать все элементы. Массив растет, если пропускная способность больше не достаточна для списка. Каждый раз, когда это произойдет, список скопирует все элементы в новый массив. Это происходит постоянно, и для большинства людей это не проблема с производительностью.

например. список со стандартным конструктором начинается с емкости 16, а когда вы .Add() 17-й элемент, он создает новый массив размером 32, копирует 16 старых значений и добавляет 17-й.

Разница в размерах также является причиной того, что ToArray() возвращает новый экземпляр массива вместо передачи частной ссылки.

Ответ 4

он создает новые ссылки в массиве, но это единственное, что этот метод мог и должен делать...

Ответ 5

Производительность должна пониматься в относительных терминах. Преобразование массива в список включает в себя копирование массива, и стоимость этого будет зависеть от размера массива. Но вы должны сравнить эту стоимость с другими вещами, которые делает ваша программа. Как вы получили информацию, которую нужно перенести в массив? Если это было при чтении с диска или в сетевом соединении или в базе данных, то копия массива в памяти очень маловероятна, чтобы сделать заметную разницу в времени.

Ответ 6

Это то, что в официальной документации Microsoft говорится о сложности времени List.ToArray

Элементы копируются с использованием Array.Copy, который является операцией O (n), где n равно Count.

Затем, глядя на Array.Copy, мы видим, что обычно это не клонирование данных, а использование ссылок:

Если sourceArray и destinationArray являются массивами ссылочного типа или массивами типа Object, выполняется поверхностное копирование. Мелкая копия массива - это новый массив, содержащий ссылки на те же элементы, что и исходный массив. Сами элементы или все, на что ссылаются элементы, не копируются. Напротив, глубокая копия массива копирует элементы и все, на что прямо или косвенно ссылаются элементы.

Итак, в заключение, это довольно эффективный способ получения массива из списка.

Ответ 7

Для любого вида List/ICollection, где он знает длину, он может выделять массив точно нужного размера с самого начала.

T[] destinationArray = new T[this._size];
Array.Copy(this._items, 0, destinationArray, 0, this._size);
return destinationArray;

Если ваш тип источника IEnumerable (а не List/Collection), то источник:

items = new TElement[4];
..
if (no more space) {
    TElement[] newItems = new TElement[checked(count * 2)];
    Array.Copy(items, 0, newItems, 0, count);
    items = newItems;

Он начинается с размера 4 и растет экспоненциально, удваиваясь каждый раз, когда заканчивается пространство. Каждый раз, когда он удваивается, он должен перераспределять память и копировать данные.

Если мы знаем размер исходных данных, мы можем избежать этих незначительных накладных расходов. Однако в большинстве случаев, например, размер массива <= 1024, он будет выполняться так быстро, что нам даже не нужно думать об этой детали реализации.

Ссылки: Enumerable.cs, List.cs(F12ing в них), Joe answer