views:

460

answers:

8

I'm writing a cache-eject method that essentially looks like this:

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

My question is about how Count is determined: is it just an internal int, or is it calculated by counting the elements each time its called?

+3  A: 

In case of a HashSet it's just an internal int field and even SortedSet (a binary tree based set for .net 4) has its count in an internal field.

Pent Ploompuu
+6  A: 

It is an internal value, and is not calculated. The documentation states that getting the value is an O(1) operation.

Obalix
Could you please correct the link?
Vlad
+29  A: 

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

Retrieving the value of this property is an O(1) operation.

This guarantees that accessing the Count won't iterate over the whole collection.


Edit: as many other posters suggested, IEnumerable<...>.Count() is however not guaranteed to be O(1). Use with care!

IEnumerable<...>.Count() is an extension method defined in System.Linq.Enumerable. The current implementation makes an explicit test if the counted IEnumerable<T> is indeed an instance of ICollection<T>, and makes use of ICollection<T>.Count if possible. Otherwise it traverses the IEnumerable<T> (possible making lazy evaluation expand) and counts items one by one.

I've not however found in the documentation whether it's guaranteed that IEnumerable<...>.Count() uses O(1) if possible, I only checked the implementation in .NET 3.5 with Reflector.

Vlad
Should have taken my own advice and read the Collection<T>.Count rather than just the List<T>.Count documentation! Thanks.
Bob Kaufman
@Bob: you're welcome.
Vlad
Please note that there's also a Count() extension method that works on IEnumerables. That is *not* guaranteed to be O(1).
Brian Rasmussen
@Brian: thank for your suggestion, I've expanded the answer
Vlad
@Vlad: You're welcome. While we're at it I should probably also mention that .Any() is a far better choice than .Count() == 0 for obvious reasons.
Brian Rasmussen
A: 

It's an internal int that get incremented each time a new item is added to the collection.

Eclipsed4utoo
+8  A: 

Your question does not specify a specific Collection class so...

It depends on the Collection class. ArrayList has an internal variable that tracks the count, as does List. However, it is implementation specific, and depending on the type of the collection, it could theoretically get recalculated on each call.

Nick
Yes. On "Collection" the class it is O(1) as the docs state. If you're talking about ICollection the interface, it really depends on the implementation.
John Gardner
@John - Its hard to tell because of how he worded the title. Was <Collection> meant to be a generic placeholder for any collection class, or was it intended to be the actual "Collection" class.
Nick
+5  A: 

As others have noted, Count is maintained when modifying the collection. This is nearly always the case with every collection type in the framework. This is considerably different than using the Count extension method on an IEnumerable which will enumerate the collection each time.

Also, with the newer collection classes the Count property is not virtual which means that the jitter can inline the call to the Count accessor which makes it practically the same as accessing a field. In other words, very quick.

Josh Einstein
+1 for specifying that the IEnumerable<>.Count() extension method is different. Of course, the extension method does some checking to see if the enumerable is a collection with an O(1) count before actually enumerating, but definitely something to keep in mind.
Tanzelax
+2  A: 

According to Reflector, it is implemented as

public int Count{ get; }

so it is defined by the derived type

Earlz
+1  A: 

Just a quick note. Be ware that there are two ways to count a collection in .NET 3.5 when System.Linq is used. For a normal collection, the first choice should be to use the Count property, for the reasons already described in other answers.

An alternative method, via the LINQ .Count() extension method, is also available. The intriguing thing about .Count() is that it can be called on ANY enumerable, regardless of whether the underlying class implements ICollection or not, or whether it has a Count property. If you ever do call .Count() however, be aware that it WILL iterate over the collection to dynamically generate a count. That generally results in O(n) complexity.

The only reason I wanted to note this is, using IntelliSense, it is often easy to accidentally end up using the Count() extension rather than the Count property.

jrista