Чи краще викликати ToList () або ToArray () у запитах LINQ?


518

Я часто наштовхуюсь на випадок, коли я хочу оцінити запит саме там, де я його оголошую. Це, як правило , тому що мені потрібно ітерації над ним кілька разів і це дорого обчислити. Наприклад:

string raw = "...";
var lines = (from l in raw.Split('\n')
             let ll = l.Trim()
             where !string.IsNullOrEmpty(ll)
             select ll).ToList();

Це чудово працює. Але якщо я не збираюсь змінювати результат, я можу також зателефонуватиToArray() замість ToList().

Однак мені цікаво, чи ToArray()реалізовано це за допомогою першого дзвінка, ToList()і тому воно менш ефективно в пам'яті, ніж просто виклик ToList().

Чи я божевільний? Потрібно просто зателефонувати ToArray()- надійний і надійний, знаючи, що пам'ять не буде виділена двічі?


10
Якщо ви хочете дізнатися, що відбувається за шторами в .NET, я дуже рекомендую .NET Reflector
David Hedlund

32
@DavidHedlund Я рекомендую .net вихідний код .
Gqqnbig

1
Я не погоджуюся, що stackoverflow.com/questions/6750447/c-toarray-performance є дублікатом цього питання, хоча є важливі стосунки. Як використання пам'яті (це запитання), так і продуктивність (інше питання) є цікавими та нетривіальними міркуваннями. Вони можуть бути описані окремо, але обидва повинні приймати рішення про вибір одного за іншим. Я не можу рекомендувати жодну з відповідей на це чи інше питання як вичерпну. Є кілька відповідей, які, якщо їх взяти разом, дають досить повне обговорення того, як вибрати один над іншим.
steve

1
@Gqqnbig - найкорисніший коментар будь-коли! Дякую :-)
Марк Купер

Відповіді:


365

Якщо вам просто не потрібен масив для задоволення інших обмежень, які ви повинні використовувати ToList. У більшості сценаріїв ToArrayбуде виділено більше пам'яті, ніж ToList.

Обидва використовують масиви для зберігання, але ToListмають більш гнучкі обмеження. Для цього масив повинен бути принаймні таким же великим, як кількість елементів у колекції. Якщо масив більший, це не проблема. Однак ToArrayмасив повинен бути розміщений точно на кількість елементів.

Для задоволення цього обмеження ToArrayчасто виконується ще одне виділення, ніж ToList. Після того, як у нього є масив, який є достатньо великим, він виділяє масив, який має саме правильний розмір, і копіює елементи назад у цей масив. Єдиного разу, коли цього можна уникнути, це коли алгоритм зростання масиву просто збігається з кількістю елементів, які потрібно зберегти (безумовно, у меншості).

EDIT

Пару людей запитали мене про наслідки наявності зайвої невикористаної пам'яті у List<T>значенні.

Це справжня турбота. Якщо створена колекція довгоживуча, після її створення ніколи не змінюється і має високі шанси висадитися в групу Gen2, то вам може бути краще взяти додатковий розподіл ToArrayвперед.

Взагалі, хоча я вважаю, що це рідше. Набагато частіше бачити безліч ToArrayдзвінків, які негайно передаються іншим короткочасним використання пам’яті, і в цьому випадку ToListявно краще.

Ключовим тут є профіль, профіль, а потім ще кілька профілів.


14
З іншого боку, невже додаткова пам'ять, виділена для роботи зі створення масиву, не підходить для збору сміття, тоді як додаткові накладні витрати для Списку залишаться? Я кажу, щоб це було простіше. Якщо вам потрібно додати або видалити елементи, є інструмент для цього. Якщо цього не зробити, для цього є інший інструмент. Використовуйте той, який має сенс. Якщо згодом ви виявите проблему з пам’яттю та продуктивністю, і це все , змініть її.
Ентоні Пеграм

1
@AnthonyPegram так, це важливо врахувати. Якщо значення використовується в довгостроковому зберіганні, воно не буде змінено і, можливо, перетворить його в Gen 2, то вам, можливо, буде краще заплатити додатковий розподіл зараз проти забруднення купи Gen 2. IME, хоча я рідко бачу цього. Набагато частіше спостерігати передачу ToArray негайно на інший короткотривалий LINQ-запит.
JaredPar

2
@AnthonyPegram я оновив свою відповідь, щоб включити цю сторону дискусії
JaredPar

8
@JaredPar Я не розумію, як ToArrayможна виділити більше пам’яті, якщо їй потрібен точний розмір місця, де, ToList<>очевидно, є автоматичні запасні місця. (autoincrease)
Royi Namir

5
@RoyiNamir, оскільки ToArray спочатку здійснює розподіл у стилі ToList із накладними витратами, а потім робить додаткове розподілення точного розміру.
Тімбо

169

Різниця в продуктивності буде незначною, оскільки List<T>реалізується як масив динамічного розміру. Виклик або ToArray()(який використовує внутрішній Buffer<T>клас для вирощування масиву), або ToList()(який викликаєList<T>(IEnumerable<T>) конструктор), в кінцевому підсумку буде питання про введення їх у масив та збільшення масиву до тих пір, поки він не підійде їм усім.

Якщо ви хочете конкретного підтвердження цього факту, перегляньте реалізацію методів, про які йдеться, у Reflector - ви побачите, що вони зменшилися до майже однакового коду.


2
Цікавим фактом, який я натрапив, є те, що для співвіднесених запитів, викликаних використанням групи, визначеної через групу приєднання до вашої проекції, змушує Linq до SQL додати ще один підзапит для отримання рахунку для цієї групи. Я припускаю, що це означає, що в цих випадках розмір колекції буде відомий до отримання елементів, і, таким чином, можна буде створити масив точно визначеного розміру, що дозволить заощадити на ресурсах обробки та пам’яті при матеріалізації результатів.
jpierson

133
Якщо графа відомо заздалегідь, продуктивність однакова. Однак, якщо граф невідомо заздалегідь, єдина відмінність між ToArray()і в ToList()тому , що перший має урізати надлишок, який включає в себе копіювання всього масиву, в той час як останній не обрізати надлишок, але використовує в середньому 25 % більше пам'яті. Це матиме наслідки, лише якщо тип даних великий struct. Просто їжа для роздумів.
Скотт Ріппей

9
@EldritchConundrum 25% випливає з цієї логіки: Якщо кількість елементів невідомо, виклик ToListабо ToArrayпочнеться зі створення невеликого буфера. Коли цей буфер заповнений, він подвоює ємність буфера і продовжує. Оскільки ємність завжди подвоюється, невикористаний буфер завжди буде від 0% до 50%.
Скотт Ріппі

2
@ScottRippey Я просто шукав джерело нового списку з джерела IEnumerable, і він перевіряє, чи є IEnumerable ICollection, і якщо він є, то він починається з виділення одного масиву з точним розміром, необхідним для властивості Count, так що це був би випадок, коли ToList (), безумовно, був би швидшим. Повна відповідь може включати цей факт, хоча я не думаю, що це найчастіший випадок.
AndyClaw

3
@AndyClaw Обидва Listі Bufferперевірять ICollection, у цьому випадку продуктивність буде однаковою.
Скотт Ріппей

54

(через сім років ...)

Кілька інших (хороших) відповідей зосередилися на мікроскопічних відмінностях у роботі, які будуть відбуватися.

Цей пост є лише доповненням до згадки про семантичну різницю, що існує між IEnumerator<T>виробленим масивом ( T[]) порівняно з поверненим a List<T>.

Найкраще проілюстровано прикладом:

IList<int> source = Enumerable.Range(1, 10).ToArray();  // try changing to .ToList()

foreach (var x in source)
{
  if (x == 5)
    source[8] *= 100;
  Console.WriteLine(x);
}

Вищевказаний код буде працювати без винятку і дасть результат:

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 рамки фактичний перелік, що використовується тут, є непублічним довідковим типом, System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]але, звичайно, це детальна інформація про реалізацію.

Тепер, якщо я міняю .ToArray()в .ToList(), я отримую тільки:

1
2
3
4
5

після чого System.InvalidOperationExceptionпідірване висловлювання:

Колекція була модифікована; Операція перерахування може не виконуватися.

Основним перелічувачем у цьому випадку є загальнодоступний тип змінних System.Collections.Generic.List`1+Enumerator[System.Int32]( IEnumerator<int>у цьому випадку ящик усередині поля, оскільки я використовую IList<int>).

На закінчення, перелік, що виробляється,List<T>відслідковує, чи змінюється список під час перерахування, тоді як створений ним перелікT[]не робить. Тому врахуйте цю різницю, обираючи між.ToList()та.ToArray().

Люди часто додають одну додаткову .ToArray() або .ToList()обходять колекцію, яка відслідковує, чи була вона модифікована протягом життя перерахувача.

(Якщо хто -то хоче знати , якList<> відстежує від того, чи був змінений збір, є приватні поля _versionв цьому класі , який змінюється кожен раз List<>оновлюється.)


28

Я погоджуюся з @mquander, що різниця в продуктивності повинна бути незначною. Однак я хотів, щоб це було визначено, щоб це було впевнено, і я це зробив - і це незначно.

Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList  time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List

Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList  time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List

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

Мій висновок: ви також можете використовувати ToList () , оскільки List<T>надає більше функціональних можливостей, ніж масив, якщо тільки кілька байтів пам’яті дійсно не мають для вас значення.


1
Цікаво, чи був би цей результат іншим, якби ви використовували великий structзамість примітивного типу чи класу.
Скотт Ріппі

12
Список <T> .ToList ???? Який сенс? Краще спробуйте ввести в нього IEnumerable, який не реалізує інтерфейс ICollection.
Григорій

8
Я хотів переконатися, що я вимірюю лише час ToListабо ToArrayдзвінка, а не перерахування жодного IEnumerable. Список <T> .ToList () все ще створює новий Список <T> - він не просто "повертає це".
EMP

23
-1 Поведінки ToArray() та ToList()занадто сильно відрізняються, коли їм надається ICollection<T>параметр. Вони просто виконують один розподіл та одну операцію копіювання. І те, List<T>і Arrayреалізація ICollection<T>, тому ваші орієнтири не дійсні.
Мохаммед Деган

1
Для всіх, хто зацікавився, я розмістив власний орієнтир як окрему відповідь . Він використовує .Select(i => i)для уникненняICollection<T> проблеми реалізацією, і включає контрольну групу, щоб побачити, скільки часу займає ітерація IEnumerable<>в першу чергу над джерелом .
StriplingWarrior

19

ToList()зазвичай є кращим, якщо ви використовуєте його IEnumerable<T>(наприклад, від ORM). Якщо довжина послідовності не відома на початку, ToArray()створює колекцію динамічної довжини, як List, а потім перетворює її в масив, що вимагає додаткового часу.


26
Я вирішив, що в цьому випадку читабельність перемагає ефективність. Зараз я використовую ToList лише тоді, коли очікую продовження додавання елементів. У всіх інших випадках (у більшості випадків) я використовую ToArray. Але дякую за внесок!
Френк Крюгер

5
Дивлячись у ILSpy, Enumerable.ToArray()дзвінки new Buffer<TSource>(source).ToArray(). У конструкторі буфера, якщо джерело реалізує ICollection, він викликає source.CopyTo (items, 0), а потім .ToArray () повертає масив внутрішніх елементів безпосередньо. Таким чином, не відбувається перетворення, яке займе додатковий час у такому випадку. Якщо джерело не реалізує ICollection, то ToArray призведе до копії масиву, щоб обрізати зайві невикористані місця з кінця масиву, як описано вище в коментарі Скотта Ріппея.
BrandonAGr

19

Пам'ять завжди буде виділятися двічі - або щось близьке до цього. Оскільки ви не можете змінити розмір масиву, обидва методи використовуватимуть якийсь механізм для збору даних у зростаючій колекції. (Ну, Список сам по собі зростає колекцією.)

Список використовує масив як внутрішнє сховище і вдвічі збільшує ємність. Це означає, що в середньому 2/3 предметів було перерозподілено щонайменше один раз, половина тих, що перерозподіляються щонайменше, двічі, половина - принаймні тричі тощо. Це означає, що кожен предмет в середньому перерозподілявся в 1,3 рази, що не дуже часто накладні.

Пам'ятайте також, що якщо ви збираєте рядки, сама колекція містить лише посилання на рядки, самі рядки не перерозподіляються.


Це може бути питання про необізнаність, але хіба 2/3, 1/3, 1/6 логіки, яку ви окреслюєте, припускають, що масив списку може бути розширений на місці? Тобто на кінці масиву є вільний простір, щоб існуючий розподіл не потрібно переміщувати?

@JonofAllTrades: Ні, масив ніколи не розширюється, управління пам'яттю в .NET просто не робить цього. Якщо вона буде розширена на місці, не було б потреби в перерозподілі предметів.
Guffa

Ах, я бачу: елементи, які не перерозподілялися, не мали цього робити, оскільки вони були в остаточному розподілі. Усі елементи, виділені в попередніх асигнуваннях, переміщуються, але завдяки логарифмічному збільшенню довжини масиву це обчислювана частка. Дякуємо за уточнення!

19

Назовні 2020 рік, і всі використовують .NET Core 3.1, тому я вирішив запустити деякі орієнтири з Benchmark.NET.

TL; DR: ToArray () має кращі показники продуктивності та виконує кращу роботу, передаючи наміри, якщо ви не плануєте мутувати колекцію.


    [MemoryDiagnoser]
    public class Benchmarks
    {
        [Params(0, 1, 6, 10, 39, 100, 666, 1000, 1337, 10000)]
        public int Count { get; set; }

        public IEnumerable<int> Items => Enumerable.Range(0, Count);

        [Benchmark(Description = "ToArray()", Baseline = true)]
        public int[] ToArray() => Items.ToArray();

        [Benchmark(Description = "ToList()")]
        public List<int> ToList() => Items.ToList();

        public static void Main() => BenchmarkRunner.Run<Benchmarks>();
    }

Результати:


    BenchmarkDotNet=v0.12.0, OS=Windows 10.0.14393.3443 (1607/AnniversaryUpdate/Redstone1)
    Intel Core i5-4460 CPU 3.20GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
    Frequency=3124994 Hz, Resolution=320.0006 ns, Timer=TSC
    .NET Core SDK=3.1.100
      [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
      DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT


    |    Method | Count |          Mean |       Error |      StdDev |        Median | Ratio | RatioSD |   Gen 0 | Gen 1 | Gen 2 | Allocated |
    |---------- |------ |--------------:|------------:|------------:|--------------:|------:|--------:|--------:|------:|------:|----------:|
    | ToArray() |     0 |      7.357 ns |   0.2096 ns |   0.1960 ns |      7.323 ns |  1.00 |    0.00 |       - |     - |     - |         - |
    |  ToList() |     0 |     13.174 ns |   0.2094 ns |   0.1958 ns |     13.084 ns |  1.79 |    0.05 |  0.0102 |     - |     - |      32 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     1 |     23.917 ns |   0.4999 ns |   0.4676 ns |     23.954 ns |  1.00 |    0.00 |  0.0229 |     - |     - |      72 B |
    |  ToList() |     1 |     33.867 ns |   0.7350 ns |   0.6876 ns |     34.013 ns |  1.42 |    0.04 |  0.0331 |     - |     - |     104 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     6 |     28.242 ns |   0.5071 ns |   0.4234 ns |     28.196 ns |  1.00 |    0.00 |  0.0280 |     - |     - |      88 B |
    |  ToList() |     6 |     43.516 ns |   0.9448 ns |   1.1949 ns |     42.896 ns |  1.56 |    0.06 |  0.0382 |     - |     - |     120 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    10 |     31.636 ns |   0.5408 ns |   0.4516 ns |     31.657 ns |  1.00 |    0.00 |  0.0331 |     - |     - |     104 B |
    |  ToList() |    10 |     53.870 ns |   1.2988 ns |   2.2403 ns |     53.415 ns |  1.77 |    0.07 |  0.0433 |     - |     - |     136 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    39 |     58.896 ns |   0.9441 ns |   0.8369 ns |     58.548 ns |  1.00 |    0.00 |  0.0713 |     - |     - |     224 B |
    |  ToList() |    39 |    138.054 ns |   2.8185 ns |   3.2458 ns |    138.937 ns |  2.35 |    0.08 |  0.0815 |     - |     - |     256 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   100 |    119.167 ns |   1.6195 ns |   1.4357 ns |    119.120 ns |  1.00 |    0.00 |  0.1478 |     - |     - |     464 B |
    |  ToList() |   100 |    274.053 ns |   5.1073 ns |   4.7774 ns |    272.242 ns |  2.30 |    0.06 |  0.1578 |     - |     - |     496 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   666 |    569.920 ns |  11.4496 ns |  11.2450 ns |    571.647 ns |  1.00 |    0.00 |  0.8688 |     - |     - |    2728 B |
    |  ToList() |   666 |  1,621.752 ns |  17.1176 ns |  16.0118 ns |  1,623.566 ns |  2.85 |    0.05 |  0.8793 |     - |     - |    2760 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1000 |    796.705 ns |  16.7091 ns |  19.8910 ns |    796.610 ns |  1.00 |    0.00 |  1.2951 |     - |     - |    4064 B |
    |  ToList() |  1000 |  2,453.110 ns |  48.1121 ns |  65.8563 ns |  2,460.190 ns |  3.09 |    0.10 |  1.3046 |     - |     - |    4096 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1337 |  1,057.983 ns |  20.9810 ns |  41.4145 ns |  1,041.028 ns |  1.00 |    0.00 |  1.7223 |     - |     - |    5416 B |
    |  ToList() |  1337 |  3,217.550 ns |  62.3777 ns |  61.2633 ns |  3,203.928 ns |  2.98 |    0.13 |  1.7357 |     - |     - |    5448 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() | 10000 |  7,309.844 ns | 160.0343 ns | 141.8662 ns |  7,279.387 ns |  1.00 |    0.00 | 12.6572 |     - |     - |   40064 B |
    |  ToList() | 10000 | 23,858.032 ns | 389.6592 ns | 364.4874 ns | 23,759.001 ns |  3.26 |    0.08 | 12.6343 |     - |     - |   40096 B |

    // * Hints *
    Outliers
      Benchmarks.ToArray(): Default -> 2 outliers were removed (35.20 ns, 35.29 ns)
      Benchmarks.ToArray(): Default -> 2 outliers were removed (38.51 ns, 38.88 ns)
      Benchmarks.ToList(): Default  -> 1 outlier  was  removed (64.69 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (67.02 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (130.08 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  detected (541.82 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (7.82 us)

    // * Legends *
      Count     : Value of the 'Count' parameter
      Mean      : Arithmetic mean of all measurements
      Error     : Half of 99.9% confidence interval
      StdDev    : Standard deviation of all measurements
      Median    : Value separating the higher half of all measurements (50th percentile)
      Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
      RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
      Gen 0     : GC Generation 0 collects per 1000 operations
      Gen 1     : GC Generation 1 collects per 1000 operations
      Gen 2     : GC Generation 2 collects per 1000 operations
      Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
      1 ns      : 1 Nanosecond (0.000000001 sec)

1
Якщо ви не плануєте мутувати колекцію, я думаю, що наміри можуть бути краще показані за допомогою ToImmutableArray()(із пакета System.Collections.Imumutable) 😉
Arturo Torres Sánchez

@ ArturoTorresSánchez правда, але якщо колекція не відкривається поза методом, я б просто використав масив.
Тиррз

2
Дякую за це Обрана відповідь є простим аргументом і припускаючи результати, що слідують за цим аргументом. Щоб зробити це науково і як бонус знати, яка різниця є, існує лише один реальний спосіб знати.
Йонас

15

Редагувати : Остання частина відповіді не вірна. Однак решта все-таки корисна інформація, тому я її залишу.

Я знаю, що це стара публікація, але після того ж запитання і проведення деяких досліджень я знайшов щось цікаве, що, можливо, варто поділитися.

По-перше, я згоден з @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>.


16
Я зробив тест і дізнався щось дивне. Масив НЕ реалізує IList <T>! Використовуючи Reflector для аналізу System.Array виявляє лише ланцюг успадкування IList, ICollection, IEnumerable, але за допомогою відображення часу виконання я з’ясував, що рядок [] має ланцюг успадкування IList, ICollection, IEnumerable, IList <string>, ICollection <string >, IE чимало <рядок>. Тому я не маю кращої відповіді, ніж @mquander!
Скотт Ріппей,

@ScottRippey Так. Незвичайне спостереження, яке ви помітили, насправді є частиною "злому" - і воно має досить дивні наслідки щодо "фіксованого розміру" та подібних властивостей (з деякими невідповідностями залежно від того, як ви його накинули). Є кілька досить великих коментарів, що стосуються цієї теми всередині вихідного коду .net. Вибачте за те, що не зв’язуюсь, але якщо я правильно пам’ятаю, знайти його досить просто (всередині класу масиву). (І також є велике питання
ТА, що

@ScottRippey просто FYI я знайшов цю відповідь , який повинен зробити свій коментар: stackoverflow.com/a/4482567/2063755
Девід Klempfner

14

Я виявив, що інших показників людей тут не вистачає, тому ось мій тріщин. Повідомте мене, якщо ви знайдете щось не так з моєю методикою.

/* This is a benchmarking template I use in LINQPad when I want to do a
 * quick performance test. Just give it a couple of actions to test and
 * it will give you a pretty good idea of how long they take compared
 * to one another. It's not perfect: You can expect a 3% error margin
 * under ideal circumstances. But if you're not going to improve
 * performance by more than 3%, you probably don't care anyway.*/
void Main()
{
    // Enter setup code here
    var values = Enumerable.Range(1, 100000)
        .Select(i => i.ToString())
        .ToArray()
        .Select(i => i);
    values.GetType().Dump();
    var actions = new[]
    {
        new TimedAction("ToList", () =>
        {
            values.ToList();
        }),
        new TimedAction("ToArray", () =>
        {
            values.ToArray();
        }),
        new TimedAction("Control", () =>
        {
            foreach (var element in values)
            {
                // do nothing
            }
        }),
        // Add tests as desired
    };
    const int TimesToRun = 1000; // Tweak this as necessary
    TimeActions(TimesToRun, actions);
}


#region timer helper methods
// Define other methods and classes here
public void TimeActions(int iterations, params TimedAction[] actions)
{
    Stopwatch s = new Stopwatch();
    int length = actions.Length;
    var results = new ActionResult[actions.Length];
    // Perform the actions in their initial order.
    for (int i = 0; i < length; i++)
    {
        var action = actions[i];
        var result = results[i] = new ActionResult { Message = action.Message };
        // Do a dry run to get things ramped up/cached
        result.DryRun1 = s.Time(action.Action, 10);
        result.FullRun1 = s.Time(action.Action, iterations);
    }
    // Perform the actions in reverse order.
    for (int i = length - 1; i >= 0; i--)
    {
        var action = actions[i];
        var result = results[i];
        // Do a dry run to get things ramped up/cached
        result.DryRun2 = s.Time(action.Action, 10);
        result.FullRun2 = s.Time(action.Action, iterations);
    }
    results.Dump();
}

public class ActionResult
{
    public string Message { get; set; }
    public double DryRun1 { get; set; }
    public double DryRun2 { get; set; }
    public double FullRun1 { get; set; }
    public double FullRun2 { get; set; }
}

public class TimedAction
{
    public TimedAction(string message, Action action)
    {
        Message = message;
        Action = action;
    }
    public string Message { get; private set; }
    public Action Action { get; private set; }
}

public static class StopwatchExtensions
{
    public static double Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Restart();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.Elapsed.TotalMilliseconds;
    }
}
#endregion

Ви можете завантажити скрипт LINQPad тут .

Результати: Продуктивність ToArray проти ToList

Підібравши код вище, ви виявите, що:

  1. Різниця менш значна при роботі з меншими масивами . Більше ітерацій, але менших масивів
  2. Різниця є менш суттєвою при роботі з ints, а не з strings.
  3. Використання великих structs замість strings займає значно більше часу, але насправді це не сильно змінює співвідношення.

Це узгоджується з висновками голосових відповідей:

  1. Ви навряд чи помітите різницю в ефективності, якщо ваш код часто не створює багато великих списків даних. (Було лише 200 мс різниці при створенні 1000 списків 100K рядків за штуку.)
  2. ToList() послідовно працює швидше, і буде кращим вибором, якщо ви довго не плануєте триматися на результатах.

Оновлення

@JonHanna зазначила, що залежно від впровадження Selectможливо перед ToList()або ToArray()реалізацією передбачити розмір отриманої колекції достроково. Заміна .Select(i => i)коду вище Where(i => true) приводить до дуже схожих результатів на даний момент, і, швидше за все, це буде робити незалежно від реалізації .NET.

Бенчмарк, використовуючи Де, а не Вибрати


У .NET Core обидва випадки повинні бути тут кращими, ніж на netfx, тому що вони зрозуміють, який розмір буде, 100000і використовуватимуть його для оптимізації обох, ToList()і ToArray(), ToArray()будучи дуже легким, тому що йому не потрібна операція скорочення, яка знадобиться. в іншому випадку ToList()перевагу має саме одне місце . Приклад у запитанні все-таки втрачається, оскільки Whereне можна робити прогнозування таких розмірів.
Джон Ханна

@JonHanna: Дякую за швидкий відгук. Я не знав. NET Core робив цю оптимізацію. Круто. У моєму коді .Select(i => i)може бути замінено .Where(i => true)на виправити це.
StriplingWarrior

Так, це зупинить оптимізацію, що впливає на неї на corefx. Можливо, буде цікавим як розмір, який має потужність у два (що має дати ToArray()перевагу), так і такий, який не є, як зазначено вище, та порівнювати результати.
Джон Ханна

@JonHanna: Цікаво, що ToArray() все-таки програє в найкращому випадку. З Math.Pow(2, 15)елементами, це (ToList: 700ms, ToArray: 900ms). Додавання ще одного елемента стикає його до (ToList: 925, ToArray: 1350). Цікаво, чи ToArrayвсе ще копіює масив навіть тоді, коли він вже ідеального розміру? Вони, напевно, подумали, що це досить рідкісний випадок, що це не варто додатково обумовлювати.
StriplingWarrior

Він не копіював на відповідність точного розміру, ще до того, як ми почали його оптимізувати в corefx, тому саме в ньому відбувається найбільше перерв.
Джон Ханна

12

Ви повинні базуватись на своєму рішенні ToListта ToArrayбазуватись на тому, що ідеально підходить для дизайну. Якщо ви хочете колекцію, до якої можна лише повторити та отримати доступ до них за допомогою індексу, виберіть ToArray. Якщо ви хочете отримати додаткові можливості додавання та видалення з колекції пізніше без зайвих клопотів, тоді зробіть цеToList (не дуже, що ви не можете додати до масиву, але це зазвичай не правильний інструмент для цього).

Якщо продуктивність має значення, ви також повинні врахувати, що було б швидше працювати. Реально ви не зателефонували ToListабо ToArrayмільйон разів, але, можливо, працюєте над отриманою колекцією мільйон разів. У цьому відношенні []краще, оскільки List<>є []з деякими накладними витратами. Дивіться цю тему для порівняння ефективності: який з них більш ефективний: список <int> або int []

На власних тестах деякий час тому я знайшов ToArrayшвидше. І я не впевнений, наскільки перекошені були тести. Хоча різниця в продуктивності настільки незначна, що помітно лише в тому випадку, якщо ви виконуєте ці запити в циклі мільйони разів.


2
Так - якщо компілятор знає, що ви повторюєте масив (а не IEnumerable <>), він може значно оптимізувати ітерацію.
RobSiklos

12

Дуже пізня відповідь, але я думаю, що це буде корисно для гуглерів.

Вони обидва смоктали, коли створили за допомогою linq. Вони обидва реалізують один і той же код, щоб змінити розмір буфера при необхідності . ToArrayвнутрішньо використовує клас для перетворення IEnumerable<>в масив, виділяючи масив з 4 елементів. Якщо цього недостатньо, то він подвоює розмір, створюючи новий масив, подвоюючий розмір поточного та копіюючи на нього поточний масив. Наприкінці він виділяє новий масив підрахунку ваших елементів. Якщо ваш запит повертає 129 елементів, то ToArray зробить 6 розподілів та операцій з копіювання пам'яті для створення масиву 256 елементів, і ніж інший масив із 129 для повернення. стільки для ефективності пам'яті.

ToList робить те саме, але пропускає останнє виділення, оскільки ви можете додавати елементи в майбутньому. Список не має значення, чи він створений із запиту linq або створений вручну.

для створення Список краще з пам'яттю, але гірше з процесором, оскільки список є загальним рішенням, кожна дія вимагає перевірки діапазону додатково до внутрішньої перевірки діапазону .net для масивів.

Отже, якщо ви будете повторювати набір результатів занадто багато разів, то масиви хороші, оскільки це означає менше перевірок діапазону, ніж списки, а компілятори, як правило, оптимізують масиви для послідовного доступу.

Розподіл ініціалізації списку може бути кращим, якщо при його створенні вказати параметр ємності. У цьому випадку він виділить масив лише один раз, припускаючи, що ви знаєте розмір результату. ToListlinq не визначає перевантаження для його надання, тому ми повинні створити наш метод розширення, який створює список із заданою ємністю, а потім використовує List<>.AddRange.

Щоб закінчити цю відповідь, я повинен написати наступні речення

  1. Зрештою, ви можете використовувати або ToArray, або ToList, продуктивність не буде настільки різною (див. Відповідь @EMP).
  2. Ви використовуєте C #. Якщо вам потрібна продуктивність, тоді не переживайте про те, щоб писати про код високої продуктивності, але переживайте, чи не пишете код поганої продуктивності.
  3. Завжди орієнтуйтеся на x64 для отримання високопродуктивного коду. AFAIK, x64 JIT заснований на компіляторі C ++ і робить деякі кумедні речі, такі як оптимізація хвостової рекурсії.
  4. Завдяки 4.5 ви також можете насолоджуватися оптимізацією за допомогою профілю та багатоядерним JIT.
  5. Нарешті, ви можете використовувати шаблон async / wait, щоб швидше його обробити.

Вони обидва смоктали? Чи є у вас альтернативна ідея, яка не потребує надмірного розподілу пам'яті?
nawfal

У контексті питання, так, вони обидва смокчуть, але через надмірні асигнування, і більше нічого. Для зменшення надмірного розподілу можна використовувати пов'язані списки за рахунок пам'яті та швидкості ітерації. Зрештою, це те, що ми робимо, робимо компроміси. Ще одна ідея, якщо створити список місткістю 200 (наприклад), а потім завантажити елементи. Це також зменшить надмірність, але масиви завжди швидші, тому це ще одна торгівля.
Ердоган Куртур

Створити список із 200 ? Це може уникнути зміни розміру, але я говорив про зайву пам'ять, що використовується. Ви не можете допомогти, тому що немає попередніх знань про те, яким може бути розмір. Ви вже можете вказати ємність у конструкторі a List<T>, але коли ви цього не зробите або коли не можете, ви не можете допомогти.
nawfal

2
єдині зайві дані в пам'яті - це вміст масиву, який є списком покажчиків (в даному випадку). один мільйон 64-бітних покажчиків займає стільки ж 8 Мб пам'яті, що є нічим порівняно з мільйоном об'єктів, на які вони вказують. 200 - це просто число, і воно має шанс зменшити кількість викликів для зміни розміру максимум у 5 разів. і так, ми не можемо допомогти. кращих варіантів у нас немає. Я не маю кращого рішення, але це не означає, що мені не дозволяють говорити, де проблема.
Ердоган Куртур

1
Хм, врешті-решт, саме там ви проводите лінію. Мені подобається поточна реалізація. Тон вашої відповіді змусив мене думати, що це критика, а не де проблема :)
nawfal

7

Це старе питання - але для користі користувачів, які натрапляють на нього, існує також і альтернатива «Пам'яті» The Enumerable - що призводить до кешування і зупинки багаторазового перерахування висловлювання Linq, що є ToArray () і ToList () використовуються для багатьох, навіть якщо атрибути колекції списку або масиву ніколи не використовуються.

Пам'ятка доступна в RX / System.Interactive lib, і пояснюється тут: Більше LINQ з System.Interactive

блогу Барта Де'Смета, який дуже рекомендується прочитати, якщо ви багато працюєте з Linq для об'єктів)


4

Один із варіантів - додати власний метод розширення, який повертається лише заново ICollection<T> . Це може бути краще, ніж використовувати ToListабо ToArrayколи ви не хочете використовувати ні властивості індексації масиву / списку, ні додавати / видаляти зі списку.

public static class EnumerableExtension
{
    /// <summary>
    /// Causes immediate evaluation of the linq but only if required.
    /// As it returns a readonly ICollection, is better than using ToList or ToArray
    /// when you do not want to use the indexing properties of an IList, or add to the collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumerable"></param>
    /// <returns>Readonly collection</returns>
    public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable)
    {
        //if it's already a readonly collection, use it
        var collection = enumerable as ICollection<T>;
        if ((collection != null) && collection.IsReadOnly)
        {
            return collection;
        }
        //or make a new collection
        return enumerable.ToList().AsReadOnly();
    }
}

Тестові одиниці:

[TestClass]
public sealed class EvaluateLinqTests
{
    [TestMethod]
    public void EvalTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResult = list.Select(i => i);
        var linqResultEvaluated = list.Select(i => i).Evaluate();
        list.Clear();
        Assert.AreEqual(0, linqResult.Count());
        //even though we have cleared the underlying list, the evaluated list does not change
        Assert.AreEqual(3, linqResultEvaluated.Count());
    }

    [TestMethod]
    public void DoesNotSaveCreatingListWhenHasListTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //list is not readonly, so we expect a new list
        Assert.AreNotSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasReadonlyListTest()
    {
        var list = new List<int> {1, 2, 3}.AsReadOnly();
        var linqResultEvaluated = list.Evaluate();
        //list is readonly, so we don't expect a new list
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasArrayTest()
    {
        var list = new[] {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //arrays are readonly (wrt ICollection<T> interface), so we don't expect a new object
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantAddToResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Add(4);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantRemoveFromResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Remove(1);
    }
}

Варто зазначити, що договір колекції лише для читання лише передбачає, що користувач об'єкта може не змінювати його, але власник може все-таки це зробити, якщо він зберігатиме посилання на нього, що пропонує інтерфейс, що змінюється. Для інтерфейсів, які гарантують, що основна структура ніколи не зміниться, подивіться на незмінні колекції. Щодо того, чому незмінні або лише для читання або звичайні колекції для читання та запису є кращими чи гіршими, для порівняння потрібен орієнтир; остаточної відповіді немає (інакше нам не доведеться вибирати).
TNE

@tne Примітка: Я роблю Tolist перед AsReadOnly, тому немає посилань на основні змінні.
weston

Ви абсолютно праві, і це був, мабуть, найкращий спосіб робити речі до того, як непорушні колекції надійшли до BCL (я бачу, що перша бета-версія вийшла через місяць після вашої відповіді).
TNE

Для безпеки потоків існують незмінні колекції, де потоки можуть припускати, що вона не зміниться, і якщо це станеться, буде створена нова версія замість того, щоб бігти проти читачів і змінювати її, поки вони нею користуються. Таким чином нікому ніколи не потрібно придбати замок.
doug65536

4

ToListAsync<T>() є кращим.

У Entity Framework 6 обидва способи врешті-решт викликають один і той же внутрішній метод, але ToArrayAsync<T>()викликає list.ToArray()в кінці, який реалізується як

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

Таким чином, ToArrayAsync<T>()має деякі накладні витрати, тому ToListAsync<T>()є кращим.


1
Це насправді відповідь, яку я шукав, як EF це робить. Мені буде цікаво, як це відбувається в EF Core.
Шиммі Вайцхандлер

3

Старе питання, але нові запитувачі в усі часи.

За даними джерела System.Linq.Eumerable , ToListпросто поверніть a new List(source), тоді як ToArrayвикористовуйте a new Buffer<T>(source).ToArray()для повернення a T[].

Про розподіл пам'яті:

Працюючи на IEnumerable<T>єдиному об'єкті, ToArrayвиділіть пам'ять ще один раз, ніж ToList. Але вам не доведеться дбати про це в більшості випадків, оскільки GC зробить збирання сміття, коли це буде потрібно.

Про ефективність виконання:

Ті, хто ставить це запитання, можуть виконати наступний код на власній машині, і ви отримаєте свою відповідь.

class PersonC
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

struct PersonS
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

class PersonT<T> : IEnumerable<T>
{
    private List<T> items;
    public PersonT(IEnumerable<T> init)
    {
        items = new List<T>(init);
    }

    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
}

private IEnumerable<PersonC> C(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonC
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private IEnumerable<PersonS> S(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonS
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private void MakeLog(string test, List<long> log) =>
    Console.WriteLine("{0} {1} ms -> [{2}]",
        test,
        log.Average(),
        string.Join(", ", log)
    );

private void Test1(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    MakeLog("C.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test2(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC1 = new PersonT<PersonC>(C(count));
    var dataS1 = new PersonT<PersonS>(S(count));

    MakeLog("C1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test3(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC2 = (ICollection<PersonC>) new List<PersonC>(C(count));
    var dataS2 = (ICollection<PersonS>) new List<PersonS>(S(count));

    MakeLog("C2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void TestMain()
{
    const int times = 100;
    const int count = 1_000_000 + 1;
    Test1(times, count);
    Test2(times, count);
    Test3(times, count);
}

Ці результати я отримав на своїй машині:

Група1:

C.ToList 761.79 ms -> [775, 755, 759, 759, 756, 759, 765, 750, 757, 762, 759, 754, 757, 753, 763, 753, 759, 756, 768, 754, 763, 757, 757, 777, 780, 758, 754, 758, 762, 754, 758, 757, 763, 758, 760, 754, 761, 755, 764, 847, 952, 755, 747, 763, 760, 758, 754, 763, 761, 758, 750, 764, 757, 763, 762, 756, 753, 759, 759, 757, 758, 779, 765, 760, 760, 756, 760, 756, 755, 764, 759, 753, 757, 760, 752, 764, 758, 760, 758, 760, 755, 761, 751, 753, 761, 762, 761, 758, 759, 752, 765, 756, 760, 755, 757, 753, 760, 751, 755, 779]
C.ToArray 782.56 ms -> [783, 774, 771, 771, 773, 774, 775, 775, 772, 770, 771, 774, 771, 1023, 975, 772, 767, 776, 771, 779, 772, 779, 775, 771, 775, 773, 775, 771, 765, 774, 770, 781, 772, 771, 781, 762, 817, 770, 775, 779, 769, 774, 763, 775, 777, 769, 777, 772, 775, 778, 775, 771, 770, 774, 772, 769, 772, 769, 774, 775, 768, 775, 769, 774, 771, 776, 774, 773, 778, 769, 778, 767, 770, 787, 783, 779, 771, 768, 805, 780, 779, 767, 773, 771, 773, 785, 1044, 853, 775, 774, 775, 771, 770, 769, 770, 776, 770, 780, 821, 770]
S.ToList 704.2 ms -> [687, 702, 709, 691, 694, 710, 696, 698, 700, 694, 701, 719, 706, 694, 702, 699, 699, 703, 704, 701, 703, 705, 697, 707, 691, 697, 707, 692, 721, 698, 695, 700, 704, 700, 701, 710, 700, 705, 697, 711, 694, 700, 695, 698, 701, 692, 696, 702, 690, 699, 708, 700, 703, 714, 701, 697, 700, 699, 694, 701, 697, 696, 699, 694, 709, 1068, 690, 706, 699, 699, 695, 708, 695, 704, 704, 700, 695, 704, 695, 696, 702, 700, 710, 708, 693, 697, 702, 694, 700, 706, 699, 695, 706, 714, 704, 700, 695, 697, 707, 704]
S.ToArray 742.5 ms -> [742, 743, 733, 745, 741, 724, 738, 745, 728, 732, 740, 727, 739, 740, 726, 744, 758, 732, 744, 745, 730, 739, 738, 723, 745, 757, 729, 741, 736, 724, 744, 756, 739, 766, 737, 725, 741, 742, 736, 748, 742, 721, 746, 1043, 806, 747, 731, 727, 742, 742, 726, 738, 746, 727, 739, 743, 730, 744, 753, 741, 739, 746, 728, 740, 744, 734, 734, 738, 731, 747, 736, 731, 765, 735, 726, 740, 743, 730, 746, 742, 725, 731, 757, 734, 738, 741, 732, 747, 744, 721, 742, 741, 727, 745, 740, 730, 747, 760, 737, 740]

C1.ToList 32.34 ms -> [35, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 32, 31, 31, 31, 31, 30, 32, 31, 31, 31, 31, 32, 30, 31, 31, 31, 30, 32, 31, 31, 31, 36, 31, 31, 31, 32, 30, 31, 32, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 33, 32, 31, 32, 31, 31, 33, 31, 31, 31, 31, 31, 32, 31, 32, 31, 34, 38, 68, 42, 79, 33, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 32, 31, 32, 31, 31, 31, 32, 33, 33, 31, 31]
C1.ToArray 56.32 ms -> [57, 56, 59, 54, 54, 55, 56, 57, 54, 54, 55, 55, 57, 56, 59, 57, 56, 58, 56, 56, 54, 56, 57, 55, 55, 55, 57, 58, 57, 58, 55, 55, 56, 55, 57, 56, 56, 59, 56, 56, 56, 56, 58, 56, 57, 56, 56, 57, 56, 55, 56, 56, 56, 59, 56, 56, 56, 55, 55, 54, 55, 54, 57, 56, 56, 56, 55, 55, 56, 56, 56, 59, 56, 56, 57, 56, 57, 56, 56, 56, 56, 62, 55, 56, 56, 56, 69, 57, 58, 56, 57, 58, 56, 57, 56, 56, 56, 56, 56, 56]
S1.ToList 88.69 ms -> [96, 90, 90, 89, 91, 88, 89, 90, 96, 89, 89, 89, 90, 90, 90, 89, 90, 90, 89, 90, 89, 91, 89, 91, 89, 91, 89, 90, 90, 89, 87, 88, 87, 88, 87, 87, 87, 87, 88, 88, 87, 87, 89, 87, 87, 87, 91, 88, 87, 86, 89, 87, 90, 89, 89, 90, 89, 87, 87, 87, 86, 87, 88, 90, 88, 87, 87, 92, 87, 87, 88, 88, 88, 86, 86, 87, 88, 87, 87, 87, 89, 87, 89, 87, 90, 89, 89, 89, 91, 89, 90, 89, 90, 88, 90, 90, 90, 88, 89, 89]
S1.ToArray 143.26 ms -> [130, 129, 130, 131, 133, 130, 131, 130, 135, 137, 130, 136, 132, 131, 130, 131, 132, 130, 132, 136, 130, 131, 157, 153, 194, 364, 176, 189, 203, 194, 189, 192, 183, 140, 142, 147, 145, 134, 159, 158, 142, 167, 130, 143, 145, 144, 160, 154, 156, 153, 153, 164, 142, 145, 137, 134, 145, 143, 142, 135, 133, 133, 135, 134, 134, 139, 139, 133, 134, 141, 133, 132, 133, 132, 133, 131, 135, 132, 133, 132, 128, 128, 130, 132, 129, 129, 129, 129, 129, 128, 134, 129, 129, 129, 129, 128, 128, 137, 130, 131]

C2.ToList 3.25 ms -> [5, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3]
C2.ToArray 3.37 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 4, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3]
S2.ToList 37.72 ms -> [38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 39, 37, 37, 39, 38, 37, 37, 37, 37, 39, 38, 37, 37, 38, 37, 38, 37, 37, 38, 37, 37, 37, 38, 37, 37, 36, 37, 38, 37, 39, 37, 39, 38, 37, 38, 38, 38, 38, 38, 38, 37, 38, 38, 38, 38, 38, 37, 38, 37, 37, 38, 37, 37, 39, 41, 37, 38, 38, 37, 37, 37, 37, 38, 37, 37, 37, 40, 37, 37, 37, 37, 39, 38]
S2.ToArray 38.86 ms -> [39, 37, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 38, 38, 38, 39, 37, 38, 38, 38, 38, 38, 37, 37, 38, 37, 37, 38, 38, 40, 38, 38, 38, 38, 38, 39, 38, 38, 39, 38, 38, 39, 38, 38, 40, 38, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 39, 37, 38, 38, 39, 71, 78, 37, 37, 37, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 38, 38, 38]

Група 2:

C.ToList 756.81 ms
C.ToArray 774.21 ms
S.ToList 709.7 ms
S.ToArray 753.51 ms

C1.ToList 32.06 ms
C1.ToArray 56.58 ms
S1.ToList 89.43 ms
S1.ToArray 132.85 ms

C2.ToList 3.45 ms
C2.ToArray 3.36 ms
S2.ToList 41.43 ms
S2.ToArray 40.84 ms

Група 3:

C.ToList 756.64 ms
C.ToArray 771.56 ms
S.ToList 705.42 ms
S.ToArray 749.59 ms

C1.ToList 31.45 ms
C1.ToArray 57.03 ms
S1.ToList 91.26 ms
S1.ToArray 129.77 ms

C2.ToList 3.26 ms
C2.ToArray 3.29 ms
S2.ToList 41.57 ms
S2.ToArray 40.69 ms

Група4:

C.ToList 729.65 ms -> [749, 730, 721, 719, 723, 743, 721, 724, 727, 722, 716, 725, 723, 726, 718, 722, 731, 722, 723, 725, 723, 722, 728, 726, 728, 718, 726, 1088, 788, 737, 729, 710, 730, 728, 717, 723, 728, 721, 722, 728, 722, 736, 723, 729, 732, 724, 726, 727, 728, 728, 726, 726, 725, 727, 725, 728, 728, 718, 724, 725, 726, 724, 726, 729, 727, 722, 722, 725, 725, 728, 724, 727, 738, 717, 726, 723, 725, 725, 727, 724, 720, 726, 726, 723, 727, 730, 723, 721, 725, 727, 727, 733, 720, 722, 722, 725, 722, 725, 728, 726]
C.ToArray 788.36 ms -> [748, 740, 742, 797, 1090, 774, 781, 787, 784, 786, 786, 782, 781, 781, 784, 783, 783, 781, 783, 787, 783, 784, 775, 789, 784, 785, 778, 774, 781, 783, 786, 781, 780, 788, 778, 785, 777, 781, 786, 782, 781, 787, 782, 787, 784, 773, 783, 782, 781, 777, 783, 781, 785, 788, 777, 776, 784, 784, 783, 789, 778, 781, 791, 768, 779, 783, 781, 787, 786, 781, 784, 781, 785, 781, 780, 809, 1155, 780, 790, 789, 783, 776, 785, 783, 786, 787, 782, 782, 787, 777, 779, 784, 783, 776, 786, 775, 782, 779, 784, 784]
S.ToList 705.54 ms -> [690, 705, 709, 708, 702, 707, 703, 696, 703, 702, 700, 703, 700, 707, 705, 699, 697, 703, 695, 698, 707, 697, 711, 710, 699, 700, 708, 707, 693, 710, 704, 691, 702, 700, 703, 700, 705, 700, 703, 695, 709, 705, 698, 699, 709, 700, 699, 704, 691, 705, 703, 700, 708, 1048, 710, 706, 706, 692, 702, 705, 695, 701, 710, 697, 698, 706, 705, 707, 707, 695, 698, 704, 698, 699, 705, 698, 703, 702, 701, 697, 702, 702, 704, 703, 699, 707, 703, 705, 701, 717, 698, 695, 713, 696, 708, 705, 697, 699, 700, 698]
S.ToArray 745.01 ms -> [751, 743, 727, 734, 736, 745, 739, 750, 739, 750, 758, 739, 744, 738, 730, 744, 745, 739, 744, 750, 733, 735, 743, 731, 749, 748, 727, 746, 749, 731, 737, 803, 1059, 756, 769, 748, 740, 745, 741, 746, 749, 732, 741, 742, 732, 744, 746, 737, 742, 739, 733, 744, 741, 729, 746, 760, 725, 741, 764, 739, 750, 751, 727, 745, 738, 727, 735, 741, 720, 736, 740, 733, 741, 746, 731, 749, 756, 740, 738, 736, 732, 741, 741, 733, 741, 744, 736, 742, 742, 735, 743, 746, 729, 748, 765, 743, 734, 742, 728, 749]

C1.ToList 32.27 ms -> [36, 31, 31, 32, 31, 32, 31, 30, 32, 30, 30, 30, 34, 32, 31, 31, 31, 31, 31, 31, 31, 32, 38, 51, 68, 57, 35, 30, 31, 31, 30, 30, 33, 30, 31, 34, 31, 34, 32, 31, 31, 31, 31, 32, 30, 30, 31, 30, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 33, 31, 31, 32, 30, 30, 30, 30, 30, 33, 30, 33, 32, 31, 30, 31, 31, 32, 32, 31, 35, 31, 34, 31, 31, 32, 31, 31, 32, 31, 32, 31, 31, 35, 31, 31, 31, 31, 31, 32]
C1.ToArray 56.72 ms -> [58, 56, 57, 57, 59, 58, 58, 57, 56, 59, 57, 55, 55, 54, 56, 55, 56, 56, 57, 59, 56, 55, 58, 56, 55, 55, 55, 55, 58, 58, 55, 57, 57, 56, 57, 57, 57, 57, 59, 59, 56, 57, 56, 57, 57, 56, 57, 59, 58, 56, 57, 57, 57, 58, 56, 56, 59, 56, 59, 57, 57, 57, 57, 59, 57, 56, 57, 56, 58, 56, 57, 56, 57, 59, 55, 58, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 56, 56, 57, 56, 56, 57, 58, 57, 57, 57, 57, 57]
S1.ToList 90.72 ms -> [95, 90, 90, 89, 89, 89, 91, 89, 89, 87, 91, 89, 89, 89, 91, 89, 89, 89, 90, 89, 89, 90, 88, 89, 88, 90, 89, 90, 89, 89, 90, 90, 89, 89, 90, 91, 89, 91, 89, 90, 89, 89, 90, 91, 89, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 89, 90, 89, 91, 89, 90, 89, 90, 89, 90, 89, 96, 89, 90, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 87, 89, 90, 90, 91, 89, 91, 89, 89, 90, 91, 90, 89, 93, 144, 149, 90, 90, 89, 89, 89]
S1.ToArray 131.4 ms -> [130, 128, 127, 134, 129, 129, 130, 136, 131, 130, 132, 132, 133, 131, 132, 131, 133, 132, 130, 131, 132, 131, 130, 133, 133, 130, 130, 131, 131, 131, 132, 134, 131, 131, 132, 131, 132, 131, 134, 131, 131, 130, 131, 131, 130, 132, 129, 131, 131, 131, 132, 131, 133, 134, 131, 131, 132, 132, 131, 133, 131, 131, 130, 133, 131, 130, 134, 132, 131, 132, 132, 131, 131, 134, 131, 131, 132, 132, 131, 130, 138, 130, 130, 131, 132, 132, 130, 134, 131, 131, 132, 131, 130, 132, 133, 131, 131, 131, 130, 131]

C2.ToList 3.21 ms -> [4, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3]
C2.ToArray 3.22 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4]
S2.ToList 41.46 ms -> [42, 40, 41, 40, 42, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 39, 41, 41, 39, 40, 40, 43, 40, 39, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 43, 40, 43, 75, 76, 47, 39, 40, 40, 40, 40, 42, 40, 41, 40, 40, 40, 44, 41, 40, 42, 42, 40, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 42, 41, 40, 41, 41, 42, 42, 41, 40, 41, 41, 41, 41, 41, 40, 42, 40, 42, 41, 41, 41, 43, 41, 41, 41, 41, 42, 41]
S2.ToArray 41.14 ms -> [42, 41, 41, 40, 40, 40, 40, 41, 41, 42, 41, 42, 41, 41, 41, 42, 41, 41, 42, 41, 41, 41, 41, 41, 42, 40, 41, 40, 42, 40, 42, 41, 40, 42, 41, 41, 43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 41, 41, 41, 40, 42, 41, 41, 41, 41, 41, 40, 41, 41, 42, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, 42, 42, 42, 41, 45, 46, 41, 40, 41, 41, 42, 41, 41, 41, 41, 41, 41, 40, 41, 43, 40, 40, 40, 40, 43, 41]

Група 5:

C.ToList 757.06 ms -> [770, 752, 752, 751, 778, 763, 761, 763, 747, 758, 748, 747, 754, 749, 752, 753, 756, 762, 750, 753, 756, 749, 755, 757, 755, 756, 755, 744, 753, 758, 747, 751, 759, 751, 761, 755, 746, 752, 752, 749, 746, 752, 753, 755, 752, 755, 754, 754, 966, 937, 749, 759, 748, 747, 754, 749, 755, 750, 746, 754, 757, 752, 753, 745, 758, 755, 761, 753, 751, 755, 755, 752, 746, 756, 755, 746, 742, 751, 751, 749, 752, 751, 756, 756, 755, 742, 749, 754, 749, 756, 753, 751, 754, 752, 751, 754, 753, 749, 755, 756]
C.ToArray 772.8 ms -> [766, 772, 755, 763, 758, 767, 763, 762, 761, 768, 769, 763, 770, 757, 765, 760, 766, 759, 764, 761, 760, 777, 1102, 881, 759, 765, 758, 762, 772, 761, 758, 757, 765, 769, 769, 761, 762, 762, 763, 760, 770, 764, 760, 768, 758, 766, 763, 770, 769, 761, 764, 761, 761, 767, 761, 762, 764, 757, 765, 766, 767, 771, 753, 762, 769, 768, 759, 764, 764, 760, 763, 763, 763, 763, 763, 767, 761, 771, 760, 765, 760, 758, 768, 770, 751, 771, 767, 771, 765, 763, 760, 765, 765, 769, 767, 767, 1193, 774, 767, 764]
S.ToList 704.73 ms -> [682, 708, 705, 699, 705, 704, 695, 703, 702, 699, 701, 708, 699, 702, 703, 701, 701, 699, 701, 707, 707, 700, 701, 705, 700, 697, 706, 702, 701, 706, 699, 692, 702, 697, 707, 704, 697, 698, 699, 699, 702, 703, 698, 697, 702, 703, 702, 704, 694, 697, 707, 695, 711, 710, 700, 693, 703, 699, 699, 706, 698, 701, 703, 704, 698, 706, 700, 704, 701, 699, 702, 705, 694, 698, 709, 736, 1053, 704, 694, 700, 698, 696, 701, 700, 700, 706, 706, 692, 698, 707, 703, 695, 703, 699, 694, 708, 695, 694, 706, 695]
S.ToArray 744.17 ms -> [746, 740, 725, 740, 739, 731, 746, 760, 735, 738, 740, 734, 744, 748, 737, 744, 745, 727, 736, 738, 728, 743, 745, 735, 748, 760, 739, 748, 762, 742, 741, 747, 733, 746, 758, 742, 742, 741, 724, 744, 747, 727, 740, 740, 729, 742, 757, 741, 740, 742, 726, 739, 746, 1133, 749, 737, 730, 740, 747, 733, 747, 752, 731, 747, 742, 730, 741, 749, 731, 749, 743, 730, 747, 742, 731, 737, 745, 734, 739, 735, 727, 743, 752, 731, 744, 742, 729, 740, 746, 731, 739, 746, 733, 745, 743, 733, 739, 742, 727, 737]

C1.ToList 31.71 ms -> [35, 32, 32, 30, 31, 33, 31, 32, 32, 31, 31, 32, 32, 33, 32, 31, 31, 32, 31, 32, 32, 32, 31, 32, 33, 32, 31, 31, 31, 32, 31, 34, 31, 31, 32, 33, 32, 32, 31, 32, 34, 32, 31, 32, 33, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 31, 33, 30, 31, 32, 30, 30, 33, 32, 32, 34, 31, 31, 31, 31, 32, 31, 31, 31, 31, 32, 31, 31, 33, 31, 32, 32, 32, 33, 32, 31, 31, 31, 31, 31, 32, 32, 33, 32, 31, 31, 32]
C1.ToArray 59.53 ms -> [63, 57, 58, 58, 57, 59, 59, 57, 60, 131, 127, 67, 58, 56, 59, 56, 57, 58, 58, 58, 57, 59, 60, 57, 57, 59, 60, 57, 57, 57, 58, 58, 58, 58, 57, 57, 61, 57, 58, 57, 57, 57, 57, 57, 58, 58, 58, 58, 57, 58, 59, 57, 58, 57, 57, 59, 58, 58, 59, 57, 59, 57, 56, 56, 59, 56, 56, 59, 57, 58, 58, 58, 57, 58, 59, 59, 58, 57, 58, 62, 65, 57, 57, 57, 58, 60, 59, 58, 59, 57, 58, 57, 58, 59, 58, 58, 58, 59, 60, 58]
S1.ToList 82.78 ms -> [87, 82, 83, 83, 82, 82, 83, 84, 82, 83, 84, 84, 84, 82, 82, 84, 82, 84, 83, 84, 82, 82, 82, 81, 83, 83, 83, 84, 84, 82, 82, 83, 83, 83, 82, 83, 85, 83, 82, 82, 84, 82, 82, 83, 83, 83, 82, 82, 82, 83, 82, 83, 82, 84, 82, 83, 82, 83, 82, 82, 82, 84, 82, 83, 82, 82, 86, 83, 83, 82, 83, 83, 83, 82, 84, 82, 83, 81, 82, 82, 82, 82, 83, 83, 83, 82, 83, 84, 83, 82, 83, 83, 83, 82, 83, 84, 82, 82, 83, 83]
S1.ToArray 122.3 ms -> [122, 119, 119, 120, 119, 120, 120, 121, 119, 119, 122, 120, 120, 120, 122, 120, 123, 120, 120, 120, 121, 123, 120, 120, 120, 121, 120, 121, 122, 120, 123, 119, 121, 118, 121, 120, 120, 120, 119, 124, 119, 121, 119, 120, 120, 120, 120, 120, 122, 121, 123, 230, 203, 123, 119, 119, 122, 119, 120, 120, 120, 122, 120, 121, 120, 121, 120, 121, 120, 121, 120, 120, 120, 121, 122, 121, 123, 119, 119, 119, 119, 121, 120, 120, 120, 122, 121, 122, 119, 120, 120, 121, 121, 120, 121, 120, 121, 118, 118, 118]

C2.ToList 3.43 ms -> [5, 3, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 6, 4, 4, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3]
C2.ToArray 3.48 ms -> [3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3]
S2.ToList 41.47 ms -> [41, 41, 49, 67, 82, 41, 41, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 41, 40, 42, 42, 40, 40, 41, 41, 41, 40, 41, 40, 41, 40, 41, 40, 42, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 41, 41, 41, 42, 40, 41, 40, 40, 40, 42, 40, 41, 42, 41, 42, 41, 42, 40, 41, 41, 41, 41, 41, 41, 41, 41, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 40, 41, 41, 41, 41, 41, 43, 40, 40, 41, 42, 41]
S2.ToArray 40.62 ms -> [42, 41, 44, 40, 40, 40, 40, 41, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 41, 40, 41, 40, 40, 41, 42, 41, 41, 41, 40, 40, 40, 40, 40, 41, 41, 42, 40, 41, 41, 41, 41, 41, 40, 42, 40, 40, 41, 41, 41, 40, 41, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 40, 41, 42, 40, 41, 41, 42, 41, 41, 40, 41, 40, 41, 40, 41, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40]

Через обмеження stackoverflow на кількість відповідей символів вибіркові списки 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>з IEnumerable<T>. Отже, якщо ваша програма дійсно дуже чутлива до цього, вам доведеться мати профіль, профіль, профіль ! В кінці кінців ви виявите , що вузьке місце нема на ToListабо ToArray, але деталь лічильників.

І, в результаті , C2.ToList vs C2.ToArrayі S2.ToList vs S2.ToArrayпоказує , що ви дійсно не потрібно піклуватися ToListабо ToArrayна НЕ під час виконання розрахованих ICollection<T>об'єктів.

Звичайно, це лише результати на моїй машині, фактичний витрачений час цих операцій на різних машинах не буде однаковим, ви можете дізнатися на своїй машині, використовуючи код вище.

Єдина причина, з якої вам потрібно зробити вибір, полягає в тому, що у вас є конкретні потреби List<T>або T[], як описано у відповіді @Jeppe Stig Nielsen .


1

Для всіх, хто зацікавлений у використанні цього результату в іншому Linq-to-sql, такому як

from q in context.MyTable
where myListOrArray.Contains(q.someID)
select q;

тоді генерується SQL є тим самим, чи використовували ви список або масив для myListOrArray. Тепер я знаю, що деякі можуть запитати, чому навіть перераховувати перед цим твердженням, але є різниця між SQL, сформованим з IQueryable vs (Список або масив).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.