代码之家  ›  专栏  ›  技术社区  ›  Bob Kaufman

<collection>.count是否昂贵?

  •  23
  • Bob Kaufman  · 技术社区  · 15 年前

    我正在编写一个缓存弹出方法,它本质上看起来像这样:

    while ( myHashSet.Count > MAX_ALLOWED_CACHE_MEMBERS )
    {
        EjectOldestItem( myHashSet );
    }
    

    我的问题是如何 Count 决定:它只是一个 private protected int 或者它是通过计算每次调用的元素来计算的?

    8 回复  |  直到 12 年前
        1
  •  43
  •   Vlad    14 年前

    http://msdn.microsoft.com/en-us/library/ms132433.aspx :

    检索此属性的值是O(1)操作。

    这保证了访问 Count 不会遍历整个集合。


    编辑:正如许多其他海报建议的那样, IEnumerable<...>.Count() 然而 保证为O(1)。小心使用!

    IEnumerable<…>.Count()。 是在中定义的扩展方法 System.Linq.Enumerable . 当前实现将在 IEnumerable<T> 确实是 ICollection<T> 并利用 ICollection<T>.Count 如果可能的话。否则它会穿过 IEnumerable<t> (可能使延迟评估扩展)并逐个计算项目。

    但是,我在文档中没有找到是否可以保证 IEnumerable<…>.Count()。 如果可能的话,使用O(1),我只使用Reflector在.NET3.5中检查了实现。


    必要的后期添加:许多流行的容器不是从 Collection<T> 但是他们的 伯爵 属性是O(1)(即不会在整个集合中迭代)。例子是 HashSet<T>.Count (这一个很可能是OP想要问的问题) Dictionary<K, V>.Count ,请 LinkedList<T>.Count , List<T>.Count , Queue<T>.Count , Stack<T>.Count 等等。

    所有这些集合实现 i收集<t> 或者只是 ICollection 所以他们 伯爵 是的实现 ICollection<T>.Count (或) ICollection.Count )不需要执行 ICollection<t>。计数 是一个O(1)操作,但根据文档,上面提到的操作是这样做的。

    (注意:例如,一些容器, Queue<T> ,实现非泛型 集合 但不是 i收集<t> 所以他们“继承”了 伯爵 属性仅来自 集合 )

        2
  •  9
  •   Nick    15 年前

    您的问题没有指定 具体的 集合类,所以…

    它取决于集合类。arraylist和list都有一个跟踪计数的内部变量。但是,它是特定于实现的,并且根据集合的类型,理论上它可以在每次调用时重新计算。

        3
  •  7
  •   itowlson    15 年前

    它是一个内部值,不计算。这个 documentation 说明获取值是O(1)操作。

        4
  •  6
  •   Josh    15 年前

    正如其他人所指出的,在修改集合时维护计数。框架中的每个集合类型几乎都是这样。这与对将每次枚举集合的IEnumerable使用Count扩展方法大不相同。

    此外,对于较新的集合类,count属性不是虚拟的,这意味着抖动可以内联对count访问器的调用,这使得它实际上与访问字段相同。换句话说,很快。

        5
  •  4
  •   Pent Ploompuu    15 年前

    万一A HashSet 只是内部的 int 场与偶 SortedSet (基于二叉树的.NET 4集)在内部字段中有其计数。

        6
  •  3
  •   Earlz    15 年前

    根据Reflector,它实现为

    public int Count{ get; }
    

    所以它是由派生类型定义的

        7
  •  2
  •   jrista    15 年前

    只是一个简短的说明。当使用System.Linq时,有两种方法可以计算.NET 3.5中的集合。对于普通集合,第一个选择应该是使用Count属性,原因已经在其他答案中描述过。

    还提供了另一种方法,通过linq.count()扩展方法。.count()的有趣之处在于,无论基础类是否实现ICollection,或是否具有Count属性,都可以对任何可枚举的对象调用它。但是,如果您执行call.count(),请注意它将在集合上迭代以动态生成一个计数。这通常会导致O(N)复杂性。

    我要注意的唯一原因是,使用IntelliSense,通常很容易意外地使用count()扩展而不是count属性。

        8
  •  1
  •   Ryan Alford    15 年前

    它是内部的 int 每次向集合中添加新项时,该值都会递增。