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