.NET Collections

Updated on September 6th 2023 with frozen collections of .NET 8

Sometimes, the proper choice of a collection can greatly impact the performance of your application. For example, there are collection types that are more appropriate for insertions, others that allow faster lookups, and so on. Plus, you must decide if you want indexed collections or not, and if you want to have generic collections, in order to have compile-time type checking. The .NET BCL comes with several general-purpose collection classes. Here is a list of all the collection classes in .NET 8 and their intended use.

Namespace Purpose
System.Collections Basic types, non-generic collections and interfaces
System.Collections.Generic Generic interfaces and collections
System.Collections.ObjectModel Observable collections
System.Collections.Specialized Special collections
System.Collections.Concurrent Concurrent (thread-safe) collections
System.Collections.Immutable Immutable collections
System.Collection.Frozen Frozen collections

PurposeTypesDescription
Fixed size System.ArraySystem.Collections.Immutable.ImmutableArray<T> Can have multiple dimensions
FIFOs (Queues) System.Collections.Queue, System.Collections.Generic.Queue<T>, System.Collections.Concurrent.ConcurrentQueue<T>, System.Collections.Immutable.ImmutableQueue<T> First in, first out
LIFOs (Stacks) System.Collections.Stack, System.Collections.Generic.Stack<T>, System.Collections.Concurrent.ConcurrentStack<T>System.Collections.Immutable.ImmutableStack<T> Last in, first out
Linked lists System.Collections.Generic.LinkedList<T> Random inserts and deletes
Array-based System.Collections.ArrayList, System.Collections.Generic.List<T>, System.Collections.Concurrent.ConcurrentBag<T>System.Collections.Immutable.ImmutableList<T>
Customizable System.Collections.ObjectModel.Collection<T> Can override InsertItem, RemoveItem, SetItem and ClearItems to implement custom algorithms
Thread-safe System.Collections.Generic.SynchronizedCollection<T>, System.Collections.Generic.SynchronizedReadOnlyCollection<T> Thread-safe
Read-only System.Collections.ObjectModel.ReadOnlyCollection<T>System.Collections.Generic.SynchronizedReadOnlyCollection<T>, System.Collections.ObjectModel.ReadOnlyObservableCollection<T> Wrap existing (not read-only collections)
Sorted System.Collections.SortedList, System.Collections.Generic.SortedList<K, V>, System.Collections.Generic.SortedDictionary<K, V>System.Collections.Generic.SortedSet<T>
Sorted by key
Sets System.Collections.Generic.HashSet<T>System.Collections.SortedSet<T>System.Collections.Immutable.ImmutableSortedSet<T>, System.Collections.Immutable.ImmutableHashSet<T>System.Collections.Frozen.FrozenSet<T> No repetition of elements
Keyed System.Collections.Specialized.ListDictionary (for <=10 items), System.Collections.Generic.Dictionary<K, V>, System.Collections.Hashtable (index by hash), System.Collections.Generic.KeyedByTypeCollection<T> (index by type), System.Collections.Specialized.HybridDictionary (changes depending on number of elements, ListDictionary -> Hashtable), System.Collections.Specialized.OrderedDictionary (access by key or index), System.Collections.Immutable.ImmutableDictionary<K, V>, System.Collections.Concurrent.ConcurrentDictionary<K, V>System.Collections.Immutable.ImmutableSortedDictionary<K, V>System.Collections.Frozen.FrozenDictionary<K, V> Single value per key
Multiple values for a single key System.Collections.Specialized.NameValueCollection
Bits System.Collections.BitArray, System.Collections.BitVector32 (up to 32 bits only) Bit operations
Strings System.Collections.Specialized.StringDictionary, System.Collections.Specialized.StringCollection Strings
Observable System.Collections.ObjectModel.ObservableCollection<T>, System.Collections.ObjectModel.ReadOnlyObservableCollection<T> Identical to System.Collections.ObjectModel.Collection<T>, fires events upon adding, removing, modifying or clearing.
Concurrent System.Collections.Concurrent.BlockingCollection<T>System.Collections.Concurrent.ConcurrentBag<T>System.Collections.Concurrent.ConcurrentDictionary<K, V>System.Collections.Concurrent.ConcurrentQueue<T>System.Collections.Concurrent.ConcurrentStack<T> Thread-safe, lock-free
Immutable System.Collections.Immutable.ImmutableDictionary<K, V>System.Collections.Immutable.ImmutableHashSet<T>System.Collections.Immutable.ImmutableList<T>System.Collections.Immutable.ImmutableQueue<T>System.Collections.Immutable.ImmutableSortedDictionary<K, V>System.Collections.Immutable.ImmutableSortedSet<T>System.Collections.Immutable.ImmutableStack<T>System.Collections.Immutable.ImmutableArray<T> Immutable collections (see http://msdn.microsoft.com/en-us/library/dn385366.aspx)
Frozen System.Collections.Frozen.FrozenSet<T>System.Collections.Frozen.FrozenDictionary<K, V> Frozen collections


Of course, you should not expose a collection class directly because it couples you to a an implementation, instead you should use collection interfaces, all these classes implement (at least) one of them). These interfaces are:

PurposeTypes
Enumerate, count System.Collections.ICollection, System.Collections.Generic.ICollection<T>
Indexed access, add/remove (where not read-only or immutable) System.Collections.IList, System.Collections.Generic.IList<T>, System.Collections.Immutable.IImmutableList<T>System.Collections.Generic.IReadOnlyList<T>System.Collections.Generic.IReadOnlyCollection<T>
Keyed System.Collections.IDictionary, System.Collections.Specialized.IOrderedDictionary, System.Collections.Generic.IDictionary<K, V>, System.Collections.Generic.IReadOnlyDictionary<K, V>, System.Collections.Immutable.IImmutableDictionary<K, V>
Enumerate only System.Collections.IEnumerable, System.Collections.IEnumerable<T>, System.Linq.IOrderedEnumerable<T>System.Linq.ILookup<K, E> (multiple values per key)
FIFOs (Queues) System.Collections.Immutable.IImmutableQueue<T>
LIFOs (Stacks) System.Collections.Immutable.IImmutableStack<T>
Sets System.Collections.Generic.ISet<T>, System.Collections.Immutable.IImmutableSet<T>System.Collections.Generic.IReadOnlySet<T>

Some recommendations for using collections:

  • Always prefer generic collections (should be obvious by now!);
  • All collections except synchronized and concurrent are not thread-safe;
  • When using a collection where you need random inserts/removes at specific positions, prefer a linked list over an array-based implementation, as the latter requires constant allocations and copies;
  • Choose a collection that better suits your intent (no repetition of items, indexed, always sorted, ...);
  • Collections that rely on object identity may need proper implementation of Equals or GetHashCode methods; the implementation of the two should be consistent;
  • For customized object equality comparison, a custom implementation of System.Collections.Generic.IEqualityComparer<T> or System.Collections.IEqualityComparer may be passed on the constructor;
  • For sorted collections, a custom implementation of System.Collections.Generic.IComparer<T> or System.Collections.IComparer may be passed on the constructor (System.Collections.Comparer, System.Collections.CaseInsensitiveComparer are default implementations);
  • Indexed and sorted collections rely on a proper, consistent and stable implementation of GetHashCode. It cannot ever change for the same object, or things may go wrong with the collection - object may not be found, for example;
  • If number of elements is known beforehand, use the constructor that specifies the initial capacity;
  • Immutable collections always return new objects when adding or removing from an existing one;
  • Cannot add or remove from a frozen collection;
  • Avoid using value types for elements, except for keys, because they need to be copied bit by bit, unlike with reference types, where only their pointer is copied.

And that's it.

Bookmark and Share

                             

2 Comments

Add a Comment

As it will appear on the website

Not displayed

Your website