C # List <T>.ToArray производительность плохая? - PullRequest
24 голосов
/ 18 июля 2009

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

Ответы [ 6 ]

49 голосов
/ 18 июля 2009

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

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

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

EDIT

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

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

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

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

В ответ на комментарий: «Нет реконструкции объектов?» :

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

19 голосов
/ 18 июля 2009

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

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

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

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

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

4 голосов
/ 18 июля 2009

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

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

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

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

1 голос
/ 18 июля 2009

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

1 голос
/ 18 июля 2009

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

0 голосов
/ 26 января 2016

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

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

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

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 и растет в геометрической прогрессии, удваиваясь каждый раз, когда ему не хватает места. Каждый раз, когда он удваивается, ему приходится перераспределять память и копировать данные.

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

Ссылки: Enumerable.cs, List.cs (F12ing в них), ответ Джо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...