2010-03-26 91 views

回答

71

通过致电IEnumerable<T>计数我假设您指的是System.Linq.Enumerable上的扩展方法CountLength不是IEnumerable<T>上的方法,而是.Net中阵列类型的属性,例如int[]

不同之处在于性能。 Length属性保证是O(1)操作。 Count扩展方法的复杂性根据对象的运行时类型而有所不同。它将尝试通过Count属性投射到支持O(1)长度查找的若干类型,如ICollection<T>。如果没有可用的,那么它将枚举所有项目并计数它们的复杂度为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)的计数,因此该方法必须Count枚举整个集合,以确定它是多久一个C#迭代器。

+2

'List '没有Length属性 - 它有一个Count属性。数组虽然有一个“长度”。 “计数”在“ICollection”和“ICollection ”(其中IList 延伸)中指定。 – 2010-03-26 07:15:43

+0

@Jon,doh。在这里要归咎于睡眠不足。将更新 – JaredPar 2010-03-26 07:18:01

+4

如果您的'IEnumerable '是无限长的,'Count()'永远不会返回... – 2010-03-26 08:05:27

18

Jon Skeet的评论很少。

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; 
} 
+6

请注意,在.NET 4中还有另一个块来检查非泛型'ICollection'类型。 (因为它也有一个'Count'属性。) – 2010-03-26 07:37:28

+0

@Jon Skeet:谢谢 – bniwredyc 2010-03-26 07:58:30

+0

有没有人知道'使用'来得到这个方法使用的'Error'类?我似乎无法在MSDN上的任何地方找到它,除了JScript文档。 – 2012-06-10 20:50:44

1
  • 长度是固定的属性,例如的单维数组或字符串。因此,从不需要计数操作(多维数组的所有维度的大小都相乘)。这里的O(1)操作意味着无论有多少元素,检索时间总是相同的。线性搜索(与此相反)是O(n)。

  • 上ICollections Count属性(列表和列表<牛逼>,例如)可以改变的,所以它要么被上添加更新/删除操作,或当收集起来之后要求的次数已经改变。取决于对象的实现。 (除了当对象是一个ICollection类型,然后ICollection.Count属性被请求时)。LINQ的Count()方法基本上遍历每一个被调用的时间(除非对象是ICollection类型,则ICollection.Count属性被请求)。

注意IEnumerables往往不是已经定义的对象集合(如列表,数组,哈希表等),但链接后台操作,无论何时都要求他们能产生结果(被称为延迟执行)。

通常情况下,你有一个像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通过这些人走过了四次,与部门领导进行核对,按名称对它们进行排序,然后对结果对象进行计数。

这只有当个人和部门是预设值集合,而不是查询本身。

+0

我可能会补充说'.Count()> 0'和'.Any()'是一样的。 – jedmao 2012-01-09 23:03:25

+1

@sfjedi:我认为这是不一样的。当找到一个项目时,Any()停止,而Count()遍历所有项目。因此,如果有一个IEnumerable,可能延迟执行,则Any()应该优先用于空检查。 – 2012-02-09 16:15:35

+3

那么'.Any()'比'.Count()> 0'更有效吗?顺便说一句,Resharper总是抱怨'.Count()> 0'。这就是为什么我自信地提起它。 – jedmao 2012-02-09 17:47:30

相关问题