How does List<T>(IEnumerable<T>) avoid the costs of expanding the internal collection?

The interface IEnumerable<T> is just like the old interface IEnumerable. It allows a single method GetEnumerator which returns an IEnumerator<T>. This second interface has the property Current and a method for moving the index MoveNext. As you can see there aren't any properties for examining the size of the data set, so you can't very well use the interface to preallocate enough space for the elements.

Well, some strange performance improvements are going on behind the scenes. Instead of using the IEnumerable<T> interface, they are instead working with the ICollection<T> interface through a cast check operation.

ICollection<T> perfWin = ienumerable as ICollection<T>;
if ( perfWin ) { data = new T[perfWin.Count]; perfWin.CopyTo(data, 0); }

Pretty interesting trick, but when does this really come into play? Well, if you have an existing array or collection that supports IEnumerable, but also has the concept of a Count and you are trying to initialize a new collection or add/insert a range of elements, then this performance feature kicks in and gives you much better results. A great example is returning a snapshot of a collection.

public List<int> CommandIDs { get { return new List<int>( currentCommandList ); } }

I've timed circumstances where commands like this were happening a few thousand times a second because of queries by the AI system. Knowing about the ICollection<T> performance boost makes me feel a bit safer about performing the operation, but most users won't realize the performance increase. Would it make sense to provide an ICollection<T> overloaded constructor? What about providing a T[] overloaded constructor? I know for instance that the following constructor is even faster than the ICollection<T> version.

public RemoveAllList( T[] initial ) {
    data = (T[]) initial.Clone();
    count = data.Length;
}

You can get identical performance out of the MyCollection<T> override as well. Remember our collections have private access to fields.

public RemoveAllList( RemoveAllList<T> initial ) {
    // Might copy extra out of count elements...
    // Possible security considerations?
    data = (T[]) initial.data.Clone();
    count = initial.count;
}

The comments identify the two issues here. You might copy extra elements that aren't needed and you might impose a security issue. As a programmer I like to take responsibility for whether or not that is going to cause a problem, but the BCL being a general purpose API has to take extra steps. I doubt they'd want to introduce any methods that had those issues.

Published Friday, October 01, 2004 4:00 PM by Justin Rogers

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required)