новый [] или новый список <T>? - PullRequest
25 голосов
/ 30 марта 2012

Я просто думаю о стиле и производительности. Раньше я писал что-то вроде,

var strings = new List<string> { "a", "b", "c" };
var ints = new List<int> { 1, 2, 3};

Но теперь мне больше нравится этот стиль,

var strings = new [] { "a", "b", "c" }.ToList();
var ints = new [] { 1, 2, 3}.ToList();

Я предпочитаю второй стиль, но теперь подумал - стоит ли так его писать или, может, он не настолько эффективен и требует больше операций?

Ответы [ 5 ]

42 голосов
/ 30 марта 2012

Я не согласен с Дарином: они не эквивалентны с точки зрения производительности. Последняя версия должна создать новый массив, и ToList скопирует его в новый список. Версия инициализатора коллекции эквивалентна:

var tmp = new List<int>();
tmp.Add(1);
tmp.Add(2);
tmp.Add(3);
var ints = tmp;

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

Разница в производительности равна , вероятно, будет незначительной, но она не равна нулю (и не явно лучше в любом направлении - в версии массива меньше вызовов, но больше выделения ).

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

Лично я предпочитаю первую форму - я думаю, она проясняет, что вы используете список с самого начала. Другой вариант - написать собственный статический класс:

public static class Lists
{
    public static List<T> Of<T>(T item0)
    {
        return new List<T> { item0 };
    }

    public static List<T> Of<T>(T item0, T item1)
    {
        return new List<T> { item0, item1 };
    }

    public static List<T> Of<T>(T item0, T item1, T item2)
    {
        return new List<T> { item0, item1, item2 };
    }

    ... as many times as you really care about, then ...

    public static List<T> Of<T>(params T[] items)
    {
        return items.ToList();
    }
}

Тогда вы можете написать:

var ints = Lists.Of(1);
var ints = Lists.Of(1, 2, 3);
var ints = Lists.Of(1, 2, 3, 5, 6, 7, 8); // Use the params version

Это все еще дает понять, что вы используете списки, но использует преимущества вывода типов.

Вы можете считать это излишним:)

13 голосов
/ 30 марта 2012

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

Попробуйте описать код на английском языке:

объявите список строк с этим содержимым

И

объявитемассив строк с этим содержимым, а затем преобразовать его в список строк

Мне первое кажется более естественным.Хотя я признаю, что второе может быть лучше для вас.

7 голосов
/ 30 марта 2012

Пример 1 (var ints = new List {1, 2, 3};): обеспечивает накладные расходы на 31,5% (Eumerable.ToList), а List.Add () вызывает накладные расходы на 8,7%.

Гдекак пример 2. Вызывает накладные расходы 11,8% для List.ctor и 5% для Ensure Capacity.

(Результаты от Red Gate ANTS Performance Profiler)

Вы можете видеть, что var ints = newСписок {1, 2, 3};имеет больше операций для выполнения через разборку

 var intsx = new[] {1, 2, 3}.ToList();
0000003f  mov         edx,3 
00000044  mov         ecx,60854186h 
00000049  call        FFF5FD70 
0000004e  mov         dword ptr [ebp-4Ch],eax 
00000051  lea         ecx,[ebp-50h] 
00000054  mov         edx,872618h 
00000059  call        61490806 
0000005e  lea         eax,[ebp-50h] 
00000061  push        dword ptr [eax] 
00000063  mov         ecx,dword ptr [ebp-4Ch] 
00000066  call        614908E3 
0000006b  mov         ecx,dword ptr [ebp-4Ch] 
0000006e  call        dword ptr ds:[008726D8h] 
00000074  mov         dword ptr [ebp-54h],eax 
00000077  mov         eax,dword ptr [ebp-54h] 
0000007a  mov         dword ptr [ebp-40h],eax 

 var ints = new List<int> { 1, 2, 3 };
0000007d  mov         ecx,60B59894h 
00000082  call        FFF5FBE0 
00000087  mov         dword ptr [ebp-58h],eax 
0000008a  mov         ecx,dword ptr [ebp-58h] 
0000008d  call        60805DB0 
00000092  mov         eax,dword ptr [ebp-58h] 
00000095  mov         dword ptr [ebp-48h],eax 
00000098  mov         ecx,dword ptr [ebp-48h] 
0000009b  mov         edx,1 
000000a0  cmp         dword ptr [ecx],ecx 
000000a2  call        608070C0 
000000a7  nop 
000000a8  mov         ecx,dword ptr [ebp-48h] 
000000ab  mov         edx,2 
000000b0  cmp         dword ptr [ecx],ecx 
000000b2  call        608070C0 
000000b7  nop 
000000b8  mov         ecx,dword ptr [ebp-48h] 
000000bb  mov         edx,3 
000000c0  cmp         dword ptr [ecx],ecx 
000000c2  call        608070C0 
000000c7  nop 
000000c8  mov         eax,dword ptr [ebp-48h] 
000000cb  mov         dword ptr [ebp-44h],eax 
        }
6 голосов
/ 30 марта 2012

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

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

Вы должны проверить байт-код, чтобы быть уверенным в этом.

Мне не нравятся внутренние компоненты C #, так что будьте осторожны!

1 голос
/ 30 марта 2012

Мне нравятся первые версии. Но что касается производительности, я думаю, что лучше всего использовать массив и конкретно определить количество элементов, если, конечно, это возможно:

var x = new int[3] { 1, 3, 3 }.ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...