.NET Framework 1.1 Performance Guidelines - Collections
From Guidance Share
- J.D. Meier, Srinath Vasireddy, Ashish Babbar, Rico Mariani, and Alex Mackman
Analyze Your Requirements Before Choosing the Collection Type
Do you need to use a collection? Arrays are generally more efficient, particularly if you need to store value types. You should choose a collection based on the size, type of data to be stored, and usage requirements. Use the following evaluation criteria when determining which collection is appropriate:
- Do you need to sort your collection?
- Do you need to search your collection?
- Do you need to access each element by index?
- Do you need a custom collection?
Do You Need to Sort Your Collection?
If you need to sort your collection, do the following:
- Use ArrayList to bind the read-only sorted data to a data grid as a data source. This is better than using a SortedList if you only need to bind read-only data using the indexes in the ArrayList (for example, because the data needs to be displayed in a read-only data grid). The data is retrieved in an ArrayList and sorted for displaying.
- Use SortedList for sorting data that is mostly static and needs to be updated only infrequently.
- Use NameValueCollection for sorting strings.
- SortedList presorts the data while constructing the collection. This results in a comparatively expensive creation process for the sorted list, but all updates to the existing data and any small additions to the list are automatically and efficiently resorted as the changes are made. Sortedlist is suitable for mostly static data with minor updates.
Do You Need to Search Your Collection?
If you need to search your collection, do the following:
- Use Hashtable if you search your collection randomly based on a key/value pair.
- Use StringDictionary for random searches on string data.
- Use ListDictionary for sizes less than 10.
Do You Need to Access Each Element by Index?
If you need to access each element by index, do the following:
- Use ArrayList and StringCollection for zero-based index access to the data.
- Use Hashtable, SortedList, ListDictionary, and StringDictionary to access elements by specifying the name of the key.
- Use NameValueCollection to access elements, either by using a zero-based index or specifying the key of the element.
- Remember that arrays do this better than any other collection type.
Do You Need a Custom Collection?
Consider developing a custom collection to address the following scenarios:
- Develop your own custom collection if you need to marshal by reference because all standard collections are passed by value. For example, if the collection stores objects that are relevant only on the server, you might want to marshal the collection by ref rather than by value.
- You need to create a strongly typed collection for your own custom object to avoid the costs of upcasting or downcasting, or both. Note that if you create a strongly typed collection by inheriting CollectionBase or Hashtable, you still end up paying the price of casting, because internally, the elements are stored as objects.
- You need a read-only collection.
- You need to have your own custom serializing behavior for your strongly typed collection. For example, if you extend Hashtable and are storing objects that implement IDeserializationCallback, you need to customize serialization to factor for the computation of hash values during the serialization process.
- You need to reduce the cost of enumeration.
Initialize Collections to the Right Size When You Can
Initialize collections to the right size if you know exactly, or even approximately, how many items you want to store in your collection; most collection types let you specify the size with the constructor, as shown in the following example.
ArrayList ar = new ArrayList (43);
Even if the collection is able to be dynamically resized, it is more efficient to allocate the collection with the correct or approximate initial capacity (based on your tests).
Prefer to Implement IEnumerable with Optimistic Concurrency
There are two legitimate ways to implement the IEnumerable interface. With the optimistic concurrency approach, you assume that the collection will not be modified while it is being enumerated. If it is modified, you throw an InvalidOperationException. An alternate pessimistic approach is to take a snapshot of the collection in the enumerator to isolate the enumerator from changes in the underlying collection. In most general cases, the optimistic concurrency model provides better performance.
Consider Boxing Overhead
When storing value types in a collection, you should consider the overhead involved, because the boxing overhead can be excessive depending on the size of the collection and the rate of updating or accessing the data. If you do not need the functionality provided by collections, consider using arrays to avoid the boxing overhead.
Consider for Instead of foreach
Use for instead of foreach (C#) to iterate the contents of arrays or collections in performance critical code, particularly if you do not need the protections offered by foreach.
Both foreach in C# and For Each in Visual Basic .NET use an enumerator to provide enhanced navigation through arrays and collections. As discussed earlier, typical implementations of enumerators, such as those provided by the .NET Framework, will have managed heap and virtual function overhead associated with their use.
If you can use the for statement to iterate over your collection, consider doing so in performance sensitive code to avoid that overhead.
Implement Strongly Typed Collections to Prevent Casting Overhead
Implement strongly typed collections to prevent upcasting or downcasting overhead. Do so by having its methods accept or return specific types instead of the generic object type. StringCollection and StringDictionary are examples of strongly typed collections for strings.
For more information and a sample implementation, see "Walkthrough: Creating Your Own Collection Class" in Visual Basic and Visual C# Concepts on MSDN at http://msdn.microsoft.com/library/en-us/vbcon/html/vaconCreatingYourOwnCollectionClass.asp.
Be Efficient with Data in Collections
When dealing with very large numbers of objects, it becomes very important to manage the size of each object. For example, it makes little difference whether you use a short (Int16), int/Integer (Int32), or long (Int64) for a single variable, but it can make a huge difference if you have a million of them in a collection or array. Whether you are dealing with primitive types or complex user-defined objects, make sure you do not allocate more memory than you need if you will be creating a large number of these objects.