Лучше ли вызывать ToList () или ToArray () в запросах LINQ?
===========================================================

Я часто сталкиваюсь со случаем, когда хочу проверить запрос именно там, где я его объявляю. Обычно это потому, что мне нужно повторить его несколько разand это дорого вычислять. Например:

Это отлично работает.**But** если я не собираюсь изменять результат, я мог бы также`ToArray()` вместо`ToList()`.

Интересно однако`ToArray()` реализуется путем первого вызова`ToList()` и, следовательно, менее эффективно использовать память, чем просто вызов`ToList()`.

Я сумасшедший? Должен ли я просто позвонить`ToArray()` - безопасно и надежно знать, что память не будет распределяться дважды?

 вы должны использовать`ToList`. В большинстве сценариев`ToArray` выделит больше памяти, чем`ToList`.

 Оба используют массивы для хранения, но`ToList` имеет более гибкое ограничение. Массив должен быть не меньше, чем количество элементов в коллекции. Если массив больше, это не проблема. Тем не мение`ToArray` нужен размер массива, точно соответствующий количеству элементов.

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

**РЕДАКТИРОВАТ**

 Несколько человек спросили меня о последствиях наличия дополнительной неиспользуемой памяти в`List<T>` ценность

 Это действительная проблема. Если созданная коллекция является долгоживущей, никогда не изменяется после создания и имеет высокую вероятность попадания в кучу Gen2, тогда вам лучше взять дополнительное выделение`ToArray` впереди.

 В общем, я нахожу это более редким случаем. Гораздо чаще можно увидеть много`ToArray` вызовы, которые немедленно передаются другим недолгим использованиям памяти, в этом случае`ToList` явно лучше.

 Ключевым моментом здесь является профиль, профиль, а затем еще немного профиль.



 кольку вы не можете изменить размер массива, оба метода будут использовать какой-то механизм для сбора данных в растущей коллекции. (Ну, список сам по себе является растущей коллекцией.)

 Список использует массив в качестве внутреннего хранилища и удваивает емкость при необходимости. Это означает, что в среднем 2/3 предметов было перераспределено, по крайней мере, один раз, половина из них перераспределена, по крайней мере, дважды, половина из них, по крайней мере, трижды, и так далее. Это означает, что каждый элемент был в среднем перераспределен в 1,3 раза, что не сильно увеличивает накладные расходы.

 Помните также, что если вы собираете строки, сама коллекция содержит только ссылки на строки, сами строки не перераспределяются.



`ToList()` обычно предпочтительнее, если вы используете его на`IEnumerable<T>` (из ORM, например). Если длина последовательности неизвестна в начале,`ToArray()` создает коллекцию динамической длины, такую как List, а затем преобразует ее в массив, что занимает дополнительное время.



 

 Они оба отстой, когда создали с помощью linq. Они оба реализуют один и тот же код для **изменить размер буфера при необходимости**. `ToArray` внутренне использует класс для преобразования`IEnumerable<>` для массива, выделив массив из 4 элементов. Если этого недостаточно, он удваивает размер, создавая новый массив, удваивая размер текущего и копируя в него текущий массив. В конце он выделяет новый массив количества ваших предметов. Если ваш запрос возвращает 129 элементов, то ToArray сделает 6 выделений и операций копирования памяти, чтобы создать массив из 256 элементов, а затем - еще один массив из 129, который нужно вернуть. так много для эффективности памяти.

ToList делает то же самое, но пропускает последнее распределение, так как вы можете добавлять элементы в будущем. List не заботится, создан ли он из запроса linq или создан вручную.

for создание List лучше с памятью, но хуже с процессором, так как list - универсальное решение, каждое действие требует проверки диапазона в дополнение к внутренней проверке диапазона .net для массивов.

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

 Распределение инициализации списка может быть лучше, если вы укажете параметр емкости при его создании. В этом случае он будет выделять массив только один раз, при условии, что вы знаете размер результата.`ToList` of linq не определяет перегрузку для ее предоставления, поэтому нам нужно создать наш метод расширения, который создает список с заданной емкостью, а затем использует`List<>.AddRange`.

 Чтобы закончить этот ответ, я должен написать следующие предложения

 В конце вы можете использовать ToArray или ToList, производительность не будет такой разной (см. Ответ @EMP). Вы используете C #. Если вам нужна производительность, не беспокойтесь о написании кода с высокой производительностью, а не о том, чтобы не писать код с плохой производительностью. Всегда предназначайтесь для x64 для высокопроизводительного кода. AFAIK, x64 JIT основан на компиляторе C ++ и выполняет некоторые забавные вещи, такие как оптимизация хвостовой рекурсии. С 4.5 вы также можете наслаждаться оптимизацией по профилю и многоядерным JIT. Наконец, вы можете использовать шаблон async / await для быстрой обработки.

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

 Если производительность имеет значение, вы должны также подумать о том, что будет быстрее работать. Реально, вы не будете звонить`ToList` или`ToArray` миллион раз, номожет быт работаю над полученным сбором миллион раз. В этом отношении`[]` лучше, так как`List<>` является`[]` с некоторыми накладными расходами. Смотрите эту тему для сравнения эффективности: [Какой из них более эффективен: List <int> или int []](https://stackoverflow.com/questions/1168915/which-one-is-more-effecient-listint-or-int)

 В своих собственных тестах я нашел`ToArray` Быстрее. И я не уверен, насколько искажены были тесты. Разница в производительности настолько незначительна, что заметна, только если вы выполняете эти запросы в цикле миллионы раз.



 

 Несколько других (хороших) ответов были сосредоточены на микроскопических различиях в производительности.

 Этот пост просто дополнение, чтобы упомянуть **семантическая разница** который существует между`IEnumerator<T>` производится массивом `T[]`) по сравнению с возвращаемым`List<T>`.

 Лучше всего проиллюстрировано на примере:

 Вышеприведенный код будет работать без исключения и выдаст результат:

```
1
2
3
4
5
6
7
8
900
10
```
 Это показывает, что`IEnumarator<int>` возвращается`int[]` не отслеживает, был ли массив изменен с момента создания перечислителя.

 Обратите внимание, что я объявил локальную переменную`source` как`IList<int>`. Таким образом, я убедился, что компилятор C # не оптимизирует`foreach` утверждение в нечто, эквивалентное`for (var idx = 0; idx < source.Length; idx++) { /* ... */ }` петля. Это то, что может сделать компилятор C #, если я использую`var source = ...;` вместо. В моей текущей версии .NET Framework фактический перечислитель, используемый здесь, является непубличным ссылочным типом`System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]` но, конечно, это деталь реализации.

 Теперь, если я переоденусь`.ToArray()` в`.ToList()`, Я получаю только:

```
1
2
3
4
5
```
 сопровождается**`System.InvalidOperationException`** взрывное высказывание:

Collection была изменена; операция перечисления может не выполняться.

 Базовым перечислителем в этом случае является публичный изменяемый тип значения[`System.Collections.Generic.List`1+Enumerator[System.Int32]`](https://msdn.microsoft.com/en-us/library/x854yt9s.aspx) (в штучной упаковке внутри`IEnumerator<int>` box в этом случае, потому что я использую`IList<int>`).

**В заключение** перечислитель, созданный`List<T>` отслеживает, изменяется ли список во время перечисления, в то время как перечислитель создается`T[]` не. Поэтому учитывайте эту разницу при выборе между`.ToList()` а также`.ToArray()`.

 Люди часто добавляют один Дополнительные `.ToArray()` или`.ToList()` чтобы обойти коллекцию, которая отслеживает, была ли она изменена в течение срока службы перечислителя.

 (Если кто-то хочет знатька the`List<>` отслеживает, была ли изменена коллекция, есть личное поле`_version` в этом классе, который меняется каждый раз, когда`List<>` обновляется.)



 `List<T>` реализован в виде динамически изменяемого массива. Звонок либо`ToArray()` (который использует внутренний`Buffer<T>` класс для увеличения массива) или`ToList()` (который вызывает`List<T>(IEnumerable<T>)` constructor) в конечном итоге будет заключаться в том, чтобы поместить их в массив и увеличивать массив до тех пор, пока он не подойдет вс

 Если вам нужно конкретное подтверждение этого факта, проверьте реализацию соответствующих методов в Reflector - вы увидите, что они сводятся к почти идентичному коду.



 кто заинтересован в использовании этого результата в другом Linq-to-sql, например,

 затем сгенерированный SQL одинаков независимо от того, использовали ли вы List или Array для myListOrArray. Теперь я знаю, что некоторые могут спросить, почему даже перечислять перед этим оператором, но есть разница между SQL, сгенерированным из IQueryable vs (List или Array).



 что других эталонных тестов здесь не хватает, так что вот мой недостаток. Дайте мне знать, если вы нашли что-то не так с моей методологией.

Вы можете [Загрузите скрипт LINQPad здесь](http://share.linqpad.net/xx3i4r.linq).

Полученные результаты:[![]()](/imgs/0LcGU.png)

 Изменив приведенный выше код, вы обнаружите, что:

 Разница менее значима, когда [работа с меньшими массивами](http://share.linqpad.net/gmovdj.linq). [![]()](/imgs/WZqvn.png) Разница менее значительна при работе с`int`s, а не`string` S. Используя большой`struct` вместо`string`s обычно занимает намного больше времени, но не сильно меняет соотношение. Это согласуется с выводами, получившими наибольшее количество ответов:

 Вы вряд ли заметите разницу в производительности, если ваш код часто создает много больших списков данных. (При создании 1000 списков по 100 тыс. Строк в каждой разнице было только 200 мс.)`ToList()` постоянно работает быстрее и будет лучшим выбором, если вы не планируете долго держаться за результаты.Обновит@ JonHanna указал, что в зависимости от реализации`Select` это возможно для`ToList()` или`ToArray()` реализация, чтобы заранее предсказать размер результирующей коллекции. Замена`.Select(i => i)` в приведенном выше коде с`Where(i => true)`  [дает очень похожие результаты](http://share.linqpad.net/8vp5it.linq) в настоящее время, и, скорее всего, это будет сделано независимо от реализации .NET.

[![]()](/imgs/Dhccj.png)



 что разница в производительности должна быть незначительной. Тем не менее, я хотел проверить это, чтобы быть уверенным, поэтому я сделал - и это незначительно.

 Каждый исходный массив / список имел 1000 элементов. Таким образом, вы можете видеть, что разница во времени и в памяти незначительна.

 Мой вывод: вы могли бы также использовать**Составлять список(**, так как`List<T>` обеспечивает больше функциональности, чем массив, если только для вас не имеет значения несколько байт памяти.



**Редактироват**: Последняя часть этого ответа недействительна. Тем не менее, остальное все еще полезная информация, поэтому я оставлю ее.

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

 Во-первых, я согласен с @mquander и его ответом. Он прав, говоря, что по производительности они идентичны.

 Однако я использовал Reflector, чтобы взглянуть на методы в`System.Linq.Enumerable` пространство имен расширений, и я заметил очень распространенную оптимизацию.  
 По возможности,`IEnumerable<T>` источник приведен к`IList<T>` или`ICollection<T>` чтобы оптимизировать метод. Например, посмотрите на`ElementAt(int)`.

 Интересно, что Microsoft решила оптимизировать только для`IList<T>`, но нет`IList`. Похоже, Microsoft предпочитает использовать`IList<T>` интерфейс.

`System.Array` только реализует`IList`, поэтому он не выиграет ни от одной из этих оптимизаций расширения.  
 Поэтому я утверждаю, что лучше всего использовать`.ToList()` метод.  
 Если вы используете какой-либо из методов расширения или передаете список другому методу, есть вероятность, что он может быть оптимизирован для`IList<T>`.



 которые сталкиваются с ним, существует также и альтернатива 'Memoizing' для Enumerable - который имеет эффект кэширования и остановки многократного перечисления оператора Linq, что и является ToArray ( ) и ToList () используются много, хотя атрибуты коллекции списка или массива никогда не используются.

Memoize доступен в библиотеке RX / System.Interactive и объясняется здесь: [Больше LINQ с System.Interactive](http://community.bartdesmet.net/blogs/bart/archive/2010/01/07/more-linq-with-system-interactive-functional-fun-and-taming-side-effects.aspx)

(От [Блог Барта Де Смета](http://community.bartdesmet.net/blogs/bart/default.aspx) который является**очен** рекомендуется читать, если вы много работаете с Linq to Objects)



 который возвращает **@ Только для чтен** `ICollection<T>`. Это может быть лучше, чем при использовании`ToList` или`ToArray` когда вы не хотите использовать ни свойства индексации массива / списка, ни добавлять / удалять из списка.

 Единичные тесты:



`ToListAsync<T>()` является предпочтительным.

 В Entity Framework 6 оба метода в конечном итоге вызывают один и тот же внутренний метод, но`ToArrayAsync<T>()` звонки`list.ToArray()` в конце, который реализован как

Так`ToArrayAsync<T>()` имеет некоторые накладные расходы, поэто`ToListAsync<T>()` является предпочтительным.



 

 Согласно источнику [System.Linq.Enumerable](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,91ab356cd49632c6), `ToList` просто верни[`new List(source)`](http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,1b527b06dc9d0c80), в то время как`ToArray` использовать[`new Buffer<T>(source).ToArray()`](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,2a0fb8d6ec1fab3a) чтобы вернуть`T[]`.

 О распределении памяти:

 Пока работает на**`IEnumerable<T>` тольк** объект,`ToArray` выделять память больше, чем`ToList`. Но вам не нужно заботиться об этом в большинстве случаев, потому что GC будет выполнять сборку мусора при необходимости.

 Об эффективном времени выполнения:

 Те, кто задают этот вопрос, могут запустить следующий код на своей машине, и вы получите ответ.

 Я получил эти результаты на своей машине:

Группа 1

 Group2:

 Group3:

 Group4:

 Group5:

 В связи с ограничением на количество символов в стеке в ответе, образцы списков Group2 и Group3 опущены.

 Как видите, использовать на самом деле не важ`ToList` или`ToArry` в большинстве случаев

 Во время обработки вычисляется во время выполнения`IEnumerable<T>` объекты, если нагрузка, вызванная вычислениями, больше, чем выделение памяти и операции копирования`ToList` а также`ToArray`, несоответствие незначительно `C.ToList vs C.ToArray` а также`S.ToList vs S.ToArray`).

 Разница может наблюдаться только для не-рассчитанных во время выполнения`IEnumerable<T>` только объекты `C1.ToList vs C1.ToArray` а также`S1.ToList vs S1.ToArray`). Но абсолютная разница (<60 мс) все еще приемлема для одного миллиона маленьких объекто`IEnumerable<T>`. На самом деле, разница определяется реализацией`Enumerator<T>` of`IEnumerable<T>`. Итак, если ваша программа действительно очень чувствительна к этому, вы должны**profile, профиль, профиль**! Наконец вы, вероятно, обнаружите, что узкое место не на`ToList` или`ToArray`, но детализация счетчиков.

 И результат`C2.ToList vs C2.ToArray` а также`S2.ToList vs S2.ToArray` показывает, что тебе действительно не нужно заботиться`ToList` или`ToArray` не рассчитывается во время выполнения`ICollection<T>` объекты

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

 Единственная причина, по которой вам нужно сделать выбор, заключается в том, что у вас есть конкретные потребности в`List<T>` или`T[]`, как описано ответом[@ Джеппе Стиг Нильсен](https://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in-linq-queries/41246500#41246500).