【问题标题】:Difference between IEnumerable Count() and LengthIEnumerable Count() 和 Length 的区别
【发布时间】:2018-07-20 01:56:49
【问题描述】:

IEnumerableCount()Length 之间的主要区别是什么?

【问题讨论】:

    标签: c# .net ienumerable


    【解决方案1】:

    通过在IEnumerable<T> 上调用Count,我假设您指的是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) 计数的C# 迭代器,因此Count 方法必须枚举整个集合以确定它的长度。

    【讨论】:

    • List&lt;T&gt; 没有 Length 属性 - 它有 Count 属性。数组有一个LengthCountICollectionICollection&lt;T&gt; 中指定(IList&lt;T&gt; 扩展)。
    • @JonSkeet 和 @Jared - 在解析具有 5-10 个元素的短 string[] 数组的上下文中...你会建议 Array.Length 来提高性能吗?
    • @one.beat.consumer:使用 Count() 仍然是 O(1),因为数组将实现 ICollection&lt;T&gt; - 但它比直接使用 Length 效率低。如果您已经知道它是一个数组,我会使用 Length not 来提高效率,但因为我认为它更惯用。同样,对于编译时类型为ICollection&lt;T&gt; 的任何东西,我都会使用Count 属性。当编译时表达式的类型为IEnumerable&lt;T&gt;时,我会调用Count(),即使我知道它实际上是一个幕后数组。
    • @JonSkeet:谢谢。我的(愚蠢的)假设是System.Array 是一个更基础的类(类似于object);但在检查 MSDN 后,我发现它实现了几个“与集合相关”的接口。我是一名网络程序员,所以“计数”一开始更具有语义意义(比如 HTML 中的 headerdiv),但我理解你的意思,因为它在编译时明确是 string[] Length更有意义。
    • 嗨 JaredPar,如果在某个循环中我将更改此列表,您能否解释一下列表的复杂性是否仍为 O(1)?例如。 while(list.Count() &gt; 0) 一些带有添加/删除操作的逻辑。
    【解决方案2】:

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

    【讨论】:

    • 请注意,在 .NET 4 中还有另一个块可以检查非泛型 ICollection 类型。 (因为它也有一个 Count 属性。)
    • 有谁知道use 要获得此方法使用的Error 类吗?除了 JScript 文档,我似乎在 MSDN 上的任何地方都找不到它。
    【解决方案3】:
    • 长度是一个固定属性,例如一维数组或字符串。所以从来没有必要的计数操作(多维数组的大小是所有维度的乘积)。这里的 O(1) 操作意味着无论有多少元素,检索时间总是相同的。线性搜索(与此相反)是 O(n)。

    • ICollections 上的 Count 属性(例如 List 和 List)可以更改,因此必须在添加/删除操作时更新它,或者在 Collection 更改后请求 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 会四次检查人员,将他们与部门领导核对,按名称对他们进行排序,然后对结果对象进行计数。

    而且只有人和部门是预设值集合,而不是查询自己。

    【讨论】:

    • 我可能会补充一点,.Count() &gt; 0.Any() 相同。
    • @sfjedi:我认为不一样。当找到一个项目时 Any() 停止,而 Count() 遍历所有项目。因此,当有一个 IEnumerable 时,可能会延迟执行,Any() 应该优先用于空检查。
    • 那么.Any() 不会比.Count() &gt; 0 更有效率吗?顺便说一句,Resharper 总是抱怨.Count() &gt; 0。这就是为什么我充满信心地提出它。
    猜你喜欢
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 2011-06-25
    • 2015-12-16
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    相关资源
    最近更新 更多