Разница между IEnumerable Count () и длиной

В чем основные различия между IEnumerable Count() и Length?


person balalakshmi    schedule 26.03.2010    source источник


Ответы (3)


Вызывая Count на IEnumerable<T>, я предполагаю, что вы имеете в виду метод расширения Count на System.Linq.Enumerable. Length - это не метод IEnumerable<T>, а свойство типов массивов в .Net, таких как int[].

Разница в производительности. СвойствоLength гарантированно является операцией O (1). Сложность метода расширения Count зависит от типа среды выполнения объекта. Он попытается выполнить приведение к нескольким типам, которые поддерживают поиск длины O (1), например ICollection<T>, через свойство Count. Если ни один из них недоступен, он перечислит все элементы и посчитает их со сложностью O (N).

Например

int[] list = CreateSomeList();
Console.WriteLine(list.Length);  // O(1)
IEnumerable<int> e1 = list;
Console.WriteLine(e1.Count()); // O(1) 
IEnumerable<int> e2 = list.Where(x => x <> 42);
Console.WriteLine(e2.Count()); // O(N)

Значение e2 реализовано как итератор C #, который не поддерживает подсчет O (1), и, следовательно, метод Count должен перечислить всю коллекцию, чтобы определить ее длину.

person JaredPar    schedule 26.03.2010
comment
List<T> не имеет свойства Length - у него есть свойство Count. Однако у массивов есть Length. Count указан в ICollection и ICollection<T> (который IList<T> расширяет). - person Jon Skeet; 26.03.2010
comment
@JonSkeet и @Jared - В контексте анализа короткого массива string[] с, скажем, 5-10 элементами ... вы бы предложили Array.Length для повышения производительности? - person one.beat.consumer; 18.04.2012
comment
@ one.beat.consumer: все равно будет O (1) с Count(), поскольку массив будет реализовывать ICollection<T>, но это менее эффективно, чем использование Length напрямую. Если вы уже знаете, что это массив, я бы использовал Length не для повышения эффективности, а потому, что я считаю его более идиоматическим. Точно так же я бы использовал свойство Count для всего, что имеет тип времени компиляции ICollection<T>. Я бы вызвал Count(), когда выражение времени компиляции имеет тип IEnumerable<T>, даже если я знаю, что это действительно скрытый массив. - person Jon Skeet; 18.04.2012
comment
@JonSkeet: Спасибо. Мое (глупое) предположение заключалось в том, что System.Array был гораздо более базовым классом (похожим на object); но после проверки MSDN я вижу, что он реализует несколько интерфейсов, связанных с коллекциями. Я веб-программист, поэтому сначала Count имел более семантический смысл (например, header против div в HTML), но я понимаю вашу точку зрения, поскольку это явно string[] во время компиляции Length имеет больше смысла. - person one.beat.consumer; 18.04.2012
comment
Привет, JaredPar, не могли бы вы объяснить, будет ли сложность для списка по-прежнему O (1), если в каком-то цикле я буду изменять этот список? Например. while(list.Count() > 0) немного логики с действиями добавления / удаления. - person Johnny_D; 30.04.2014

Небольшое дополнение к комментарию Джона Скита.

Вот исходный код метода расширения Count():

.NET 3:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    ICollection<TSource> is2 = source as ICollection<TSource>;
    if (is2 != null)
    {
        return is2.Count;
    }
    int num = 0;
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            num++;
        }
    }
    return num;
}

.NET 4:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    ICollection<TSource> is2 = source as ICollection<TSource>;
    if (is2 != null)
    {
        return is2.Count;
    }
    ICollection is3 = source as ICollection;
    if (is3 != null)
    {
        return is3.Count;
    }
    int num = 0;
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            num++;
        }
    }
    return num;
}
person bniwredyc    schedule 26.03.2010
comment
Обратите внимание, что в .NET 4 есть еще один блок для проверки неуниверсального типа ICollection. (Поскольку это также имеет свойство Count.) - person Jon Skeet; 26.03.2010
comment
Кто-нибудь знает, что нужно use, чтобы получить Error класс, который использует этот метод? Я не могу найти его нигде в MSDN, кроме документации JScript. - person Moshe Katz; 11.06.2012

  • Длина - это фиксированное свойство, например одномерного массива или строки. Таким образом, никогда не требуется операция подсчета (многомерные массивы имеют размер, умноженный на все измерения). Операция O (1) здесь означает, что время поиска всегда одинаково, независимо от количества элементов. Линейный поиск (в отличие от этого) будет O (n).

  • Свойство Count в ICollections (например, List и List ‹T›) может изменяться, поэтому его необходимо обновлять либо при операциях добавления / удаления, либо при запросе Count после изменения коллекции. Зависит от реализации объекта.

  • Метод Count () LINQ в основном выполняет итерацию КАЖДЫЙ РАЗ, когда он вызывается (кроме случаев, когда объект является типом ICollection, тогда запрашивается свойство ICollection.Count).

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

Обычно у вас есть такой SQL-оператор, как LINQ (типичное приложение с отложенным выполнением):

IEnumerable<Person> deptLeaders = 
   from p in persons
   join d in departments
      on p.ID equals d.LeaderID
   orderby p.LastName, p.FirstName
   select p;

Затем есть такой код:

if (deptLeaders.Count() > 0)
{
   ReportNumberOfDeptLeaders(deptLeaders.Count());
   if (deptLeaders.Count() > 20)
      WarnTooManyDepartmentLeaders(deptLeaders.Count());
}

Таким образом, когда выдается предупреждение для слишком большого числа руководителей отделов, .NET ЧЕТЫРЕ РАЗ просматривает людей, сравнивает их с руководителями отделов, сортирует их по именам и затем подсчитывает объекты результатов.

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

person Erik Hart    schedule 13.08.2011
comment
Могу добавить, что .Count() > 0 - это то же самое, что .Any(). - person jedmao; 10.01.2012
comment
@sfjedi: Думаю, это не то же самое. Any () останавливается, когда элемент был найден, а Count () выполняет итерацию по всем. Поэтому при наличии IEnumerable, возможного для отложенного выполнения, Any () следует предпочесть для пустой проверки. - person Erik Hart; 09.02.2012
comment
Разве тогда .Any() не будет эффективнее, чем .Count() > 0? Кстати, Resharper всегда жалуется на .Count() > 0. Вот почему я говорю об этом с уверенностью. - person jedmao; 09.02.2012