IEnumerable Count()和Length之间的区别


Answers:


102

通过调用Count on,IEnumerable<T>我假设您是指Counton的扩展方法System.Linq.EnumerableLength不是IEnumerable<T>.Net中的方法,而是.Net中的数组类型的属性,例如int[]

区别在于性能。该Length属性保证是O(1)运算。Count扩展方法的复杂度根据对象的运行时类型而有所不同。它将尝试ICollection<T>通过Count属性将类型转换为支持O(1)长度查找的几种类型。如果没有可用的,它将枚举所有项并计数,其复杂度为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被实现为不支持O(1)计数的C#迭代器,因此该方法Count必须枚举整个集合以确定其持续时间。


6
List<T>没有Length属性-它具有Count属性。数组Length虽然有。CountICollection和中指定ICollection<T>IList<T>扩展)。
乔恩·斯基特

@JonSkeet和@Jared-在解析包含string[]5-10个元素的短数组的情况下...那么您会建议Array.Length性能吗?
one.beat.consumer 2012年

1
@ one.beat.consumer:仍将使用Count()数组实现的O(1)ICollection<T>-但效率不如Length直接使用。如果你已经知道它是一个数组,我会使用Length 不是效率,而是因为我认为这是更地道。同样,Count对于任何编译时类型为的东西,我都会使用该属性ICollection<T>。我会Count()编译时表达式的类型为时调用IEnumerable<T>,即使我知道它实际上是幕后的数组。
乔恩·斯基特

@JonSkeet:谢谢。我(愚蠢的)假设是那System.Array是一个更基础的类(类似于object);但是在检查了MSDN之后,我看到它实现了几个“与集合相关”的接口。我是一名网络程序员,所以“ Count”起初在语义上更具意义(例如headervs. divHTML),但我理解您的观点,因为它string[]在编译时显然是a的Length
one.beat.consumer 2012年

嗨,JaredPar,您能解释一下如果要在某个循环中更改此列表吗,列表的复杂度是否仍为O(1)?例如,while(list.Count() > 0)一些带有添加/删除操作的逻辑。
Johnny_D 2014年

24

小除了乔恩斯基特的评论。

这是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;
}

9
请注意,在.NET 4中,还有另一个块要检查非泛型ICollection类型。(因为它也Count有财产。)
Jon Skeet 2010年

有谁知道如何use获取Error此方法使用的类?除了JScript文档,我似乎在MSDN上都找不到它。
Moshe Katz 2012年

2
  • 长度是固定属性,例如一维数组或字符串。因此,永远不需要计数操作(多维数组的大小乘以所有维)。这里的O(1)运算意味着无论有多少个元素,检索时间始终是相同的。(与此相对)线性搜索为O(n)。

  • ICollections上的Count属性(例如List和List <T>)可以更改,因此必须在添加/删除操作上进行更新,或者在Collection更改后请求Count时进行更新。取决于对象的实现。

  • LINQ的Count()方法基本上在每次被调用时都会进行迭代(除非对象是ICollection类型,然后才请求ICollection.Count属性)。

请注意,IEnumerable通常不是已经定义的对象集合(例如列表,数组,哈希表等),而是链接到后台操作,这些后台操作在请求时会生成结果(称为延迟执行)。

通常,您有一个类似LINQ的SQL这样的语句(延迟执行的典型应用程序):

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会经过人员四次,针对部门负责人对他们进行检查,按名称对他们进行排序,然后对结果对象进行计数。

仅当人员和部门是预设值集合,而不是查询自身时。


我可能会补充说.Count() > 0.Any()
jedmao 2012年

1
@sfjedi:我觉得不一样。找到项目后,Any()停止,而Count()遍历所有项目。因此,当具有IEnumerable时,可以执行延迟执行,应该优先选择Any()进行空检查。
Erik Hart'2

4
.Any()比现在更有效.Count() > 0吗?顺便说一句,Resharper总是抱怨.Count() > 0。这就是为什么我满怀信心地提出来。
jedmao 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.