<collection> .Count是否使用昂贵?</collection>

时间:2010-02-26 20:58:35

标签: c# collections count

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

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

我的问题是如何确定Count:它只是private还是protected int,还是通过每次调用时计算元素来计算?

8 个答案:

答案 0 :(得分:44)

来自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检查了.NET 3.5中的实现。


必要的后期添加:许多流行的容器不是从Collection<T>派生的,但是它们的Count属性是O(1)(也就是说,不会遍历整个集合)。示例为HashSet<T>.Count(这很可能是OP想要询问的内容),Dictionary<K, V>.CountLinkedList<T>.CountList<T>.CountQueue<T>.Count,{{3}等等。

所有这些集合都实现ICollection<T>或仅ICollection,因此他们的CountStack<T>.Count(或ICollection<T>.Count)的实现。根据文档,ICollection<T>.Count的实现不需要是O(1)操作,但上面提到的操作是这样做的。

(请注意:某些容器,例如,ICollection.Count,实现非通用ICollection但不实现ICollection<T>,因此它们仅从“{1}}”继承“Count属性来自ICollection。)

答案 1 :(得分:9)

您的问题未指定特定的集合类,因此......

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

答案 2 :(得分:7)

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

答案 3 :(得分:6)

正如其他人所说,修改集合时会保留Count。框架中的每个集合类型几乎都是这种情况。这与在IEnumerable上使用Count扩展方法有很大不同,IEnumerable每次都会枚举该集合。

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

答案 4 :(得分:4)

如果是HashSet,它只是一个内部int字段,甚至SortedSet(基于二进制树的.net 4设置)都在内部字段中计数。

答案 5 :(得分:3)

根据Reflector,它实现为

public int Count{ get; }

因此它由派生类型

定义

答案 6 :(得分:2)

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

还可以通过LINQ .Count()扩展方法获得替代方法。关于.Count()的有趣之处在于它可以在任何可枚举上调用,无论底层类是否实现ICollection,或者它是否具有Count属性。但是,如果您曾调用过.Count(),请注意它会迭代集合以动态生成计数。这通常会导致O(n)复杂性。

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

答案 7 :(得分:1)

每次将新项目添加到集合时,内部int都会增加。