Мне нужно перебрать и посчитать. Что является самым быстрым или предпочтительным: ToArray () или ToList ()?

Возможный дубликат:
Что лучше вызывать ToList () или ToArray () в запросах LINQ?

У меня такой код:

void Foobar(string[] arr, Dictionary<string, string[]>)
{
   var t = arr.Intersect(dic.Keys).ToList(); // .or ToArray() ?
   foreach(var item in t)
   {
      ..
   }

   var j = t.Count; // also I need this
}

какой метод предпочтительнее?

Я мог бы обойтись без них, но мне нужно знать размер, и я не хочу вызывать Enumerable.Count<T>() - кажется, я выполняю больше действий, чем Array<T>.Size или List<T>.Count. Я прав?


person abatishchev    schedule 01.12.2009    source источник
comment
По теме: Лучше ли вызывать ToList () или ToArray () в запросах LINQ?   -  person GSerg    schedule 07.07.2012


Ответы (3)


Фактически, в текущей реализации MS Count (IEnumerable) есть ярлык, который проверяет, является ли IEnumerable ICollection и вызывает для него Count. Так что производительность должна быть сопоставимой по подсчету элементов.

ToList и ToArray немного похожи. Если IEnumerable является ICollection, то вместо этого вызывается метод CopyTo, что немного быстрее.

Итак, выберите, что делает ваш код наиболее читаемым, и сравните тест для ВАШЕГО варианта использования, чтобы получить однозначный ответ.

Обновление: я сделал наивный тест.

Начиная с массива: var items = Enumerable.Range(1,1000).ToArray();

  • вызов ToList (): 25 мс / 10000
  • вызов ToArray (): 23 мс / 10000

Начиная с IEnumerable: var items = Enumerable.Range(1,1000);

  • вызов ToList (): 168 мс / 10000
  • вызов ToArray (): 171 мс / 10000

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

person Yann Schwartz    schedule 01.12.2009

Если вас действительно беспокоит производительность, вам следует перебрать IEnumerable и считать по ходу. Это позволяет избежать создания новой коллекции в целом, а пересечение необходимо повторить только один раз:

void Foobar(string[] arr, Dictionary<string, string[]>)
{
   var t = arr.Intersect(dic.Keys);
   int count = 0;
   foreach(var item in t)
   {
      count++;
      ..
   }

   var j = count;
}

Но как кто-то сказал: это пахнет микрооптимизацией. Если производительность действительно имеет значение в этой ситуации, по крайней мере, выполните профилирование производительности, чтобы выяснить, какой метод действительно самый быстрый для вас.

person Greg    schedule 01.12.2009
comment
Но если вас так беспокоит производительность, то это означает, что вам нужно обновить переменную счетчика X раз для IEnumerable, который имеет X элементов - по сравнению с одним поиском для Count, который может быть более эффективным для большой коллекции. - person Yaakov Ellis; 01.12.2009
comment
@ Яаков: Что-то придется подсчитывать размер коллекции. Подсчитывая себя, вам нужно только один раз перебрать коллекцию. Если коллекция преобразуется в массив списка, она должна быть проинтегрирована как минимум дважды (один раз для преобразования и один раз для цикла foreach. - person Greg; 01.12.2009
comment
Согласно отражателю, List ‹›, ArrayList ‹› и Array содержат переменную длины или размера в объекте, на который ссылаются при выполнении прямого подсчета, поэтому запуск Count в массиве или списке не вызовет другого перечисления. - person Yaakov Ellis; 01.12.2009
comment
@ Яаков: Нет, дело в том, что создание массива или списка из перечисления вызовет итерацию для создания. Затем вам нужно будет выполнить итерацию для любой обработки, выполняемой в созданном массиве или списке. Вместо этого просто перечислите подсчет и обработку коллекции по мере продвижения. - person jason; 01.12.2009
comment
@jason Чтобы избежать повторного перечисления перечислимого для создания списка / массива, когда мне не нужны все сразу, и они нужны мне в разных местах одного и того же перечислимого, что вы предлагаете? - person staa99; 05.05.2018

Разница, вероятно, настолько мала, что стоит просто использовать метод, который лучше соответствует вашим потребностям. Запахи микрооптимизации.

И в этом случае, поскольку все, что вы делаете, это перечисление набора и подсчет набора (и то, и другое вы можете сделать с IEnumerable), почему бы просто не оставить его как IEnumerable ‹>?

person Yaakov Ellis    schedule 01.12.2009
comment
Я предполагаю, что это естественным образом приводит к другому вопросу: поскольку производительность для большинства целей и задач одинакова, какой тип я должен использовать в качестве типа коллекции по умолчанию? Я лично предпочитаю List<T>, потому что он не предназначен только для чтения, но в прошлом у меня были проблемы с убеждением других, что это лучший выбор типа коллекции по умолчанию, чем T[]. - person Roman Starkov; 05.05.2010
comment
@romkyns: конечно, это о классе по умолчанию для общего использования. и я предпочитаю массив как коллекцию фиксированной длины. Я использую динамическую длину только тогда, когда она явно нужна - person abatishchev; 09.07.2012
comment
Что вы предлагаете, чтобы избежать повторного перечисления перечислимого для создания списка / массива, когда мне не нужны все сразу и они нужны мне в разных местах одного и того же перечислимого объекта? - person staa99; 05.05.2018