Является ли метод Linq Count(
) быстрее или медленнее, чем List<>.Count
или Array.Length
?
Является ли значение Linq Count() более быстрым или медленным, чем List.Count или Array.Length?
Ответ 1
В целом медленнее. Общее число LINQ - это операция O(N)
, в то время как List.Count
и Array.Length
гарантированы как O(1)
.
Однако в некоторых случаях LINQ будет особым случаем с параметром IEnumerable<T>
путем литья определенных типов интерфейсов, таких как IList<T>
или ICollection<T>
. Затем он будет использовать этот метод Count для выполнения фактической операции Count()
. Таким образом, он вернется к O(1)
. Но вы по-прежнему оплачиваете незначительные накладные расходы на вызов трансляции и интерфейса.
Ответ 2
Метод Enumerable.Count()
проверяет ICollection<T>
, используя .Count
- поэтому в случае массивов и списков он не намного более неэффективен (просто дополнительный уровень косвенности).
Ответ 3
Марк имеет правильный ответ, но дьявол находится в деталях.На моей машине:
- Для массивов .Length примерно в 100 раз быстрее .Count()
- Для списков .Count примерно в 10 раз быстрее .Count() - Примечание: я ожидал бы подобную производительность из всех Коллекций, которые реализуют
IList<T>
Массивы начинают медленнее, так как .Length включает только одну операцию,.Count на массивах включает слой косвенности. Итак.Совокупность массивов начинается с 10x медленнее (на моей машине), что может быть одной из причин, по которой интерфейс реализован явно. Представьте, что если у вас есть объект с двумя общедоступными свойствами,.Count и .Length. Оба делают то же самое, но .Count в 10 раз медленнее.
Конечно, это не имеет большого значения, поскольку вам нужно будет подсчитывать свои массивы и перечислять миллионы раз в секунду, чтобы почувствовать удар производительности.
Код:
static void TimeAction(string description, int times, Action func) {
var watch = new Stopwatch();
watch.Start();
for (int i = 0; i < times; i++) {
func();
}
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
static void Main(string[] args) {
var array = Enumerable.Range(0, 10000000).ToArray();
var list = Enumerable.Range(0, 10000000).ToArray().ToList();
// jit
TimeAction("Ignore and jit", 1 ,() =>
{
var junk = array.Length;
var junk2 = list.Count;
array.Count();
list.Count();
});
TimeAction("Array Length", 1000000, () => {
var tmp1 = array.Length;
});
TimeAction("Array Count()", 1000000, () =>
{
var tmp2 = array.Count();
});
TimeAction("Array Length through cast", 1000000, () =>
{
var tmp3 = (array as ICollection<int>).Count;
});
TimeAction("List Count", 1000000, () =>
{
var tmp1 = list.Count;
});
TimeAction("List Count()", 1000000, () =>
{
var tmp2 = list.Count();
});
Console.ReadKey();
}
Результаты:
Array Length Time Elapsed 3 ms Array Count() Time Elapsed 264 ms Array Length through cast Time Elapsed 16 ms List Count Time Elapsed 3 ms List Count() Time Elapsed 18 ms
Ответ 4
Я считаю, что если вы вызовете Linq.Count() на ICollection или IList (например, ArrayList или List), он просто вернет значение свойства Count. Таким образом, производительность будет примерно одинаковой в простых коллекциях.
Ответ 5
Я бы сказал, что это зависит от Списка. Если IQueryable является таблицей в db где-то, то Count() будет намного быстрее, потому что ему не нужно загружать все объекты. Но если список в памяти, я бы предположил, что свойство Count будет быстрее, если не о том же.
Ответ 6
Дополнительная информация - LINQ Count - разница между ее использованием и не может быть огромной - и это не обязательно должно быть слишком большим. У меня есть коллекция, образованная из linq для объектов с примерно 6500 элементами (большая.. но не огромная никакими средствами). Count() в моем случае занимает несколько секунд. Преобразование в список (или массив, что бы то ни было), тогда счет будет фактически немедленным. Наличие этого счета во внутреннем цикле означает, что воздействие может быть огромным. Граф перечисляет все. Массив и список являются "осведомленными" о своей длине и не нуждаются в их перечислении. Любые отладочные операторы (log4net для ex), ссылающиеся на этот счетчик(), также замедляют все значительно. Сделайте себе одолжение, и если вам нужно ссылаться на это, часто сохраняйте размер счета и только вызывайте его один раз в коллекции LINQ, если вы не конвертируете его в список, а затем можете ссылаться без удара производительности.
Вот краткий тест того, о чем я говорил выше. Обратите внимание, что каждый раз, когда мы вызываем Count(), размер нашей коллекции изменяется. Следовательно, происходит оценка, которая больше, чем ожидаемая операция "count". Просто то, что нужно знать:)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LinqTest
{
class TestClass
{
public TestClass()
{
CreateDate = DateTime.Now;
}
public DateTime CreateDate;
}
class Program
{
static void Main(string[] args)
{
//Populate the test class
List list = new List(1000);
for (int i=0; i<1000; i++)
{
System.Threading.Thread.Sleep(20);
list.Add(new TestClass());
if(i%100==0)
{
Console.WriteLine(i.ToString() + " items added");
}
}
//now query for items
var newList = list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now);
while (newList.Count() > 0)
{
//Note - are actual count keeps decreasing.. showing our 'execute' is running every time we call count.
Console.WriteLine(newList.Count());
System.Threading.Thread.Sleep(500);
}
}
}
}