Collections

Namespaces

  • System.Collections — .NET Framework 1.0; Legacy;
  • System.Collections.Generic — .NET Framework 2.0; no thread synchronization
  • System.Collections.Concurrent — .NET Framework 4.0; thread-safe

Common Features

  • All collections implement the ICollection/<T> interface. This means they must have a Count property.
  • All collections implement the IEnumerable/<T> interface, which allows them to be iterated over with foreach.
    • This interface requires GetEnuemrator(), which returns an object that implements IEnumerator.
      • The IEnumerator object must have methods MoveNext() and Reset() and property Current that contains the current item in the collection.

Choosing a Collection

A collection of…Generic collectionSorted counterpart?Thread-safe counterpart?Immutable counterpart?
Key/value pairs accessed by keyDictionary<TKey,TValue>SortedDictionary<TKey,TValue>ConcurrentDictionary<TKey,TValue>ImmutableDictionary<TKey,TValue>, ImmutableSortedDictionary<TKey,TValue>
Elements accessed by indexList<T>SortedList<TKey,TValue>ConcurrentBag<T>ImmutableList<T>, ImmutableArray
Elements in FIFO orderQueue<T>N/AConcurrentQueue<T>ImmutableQueue<T>
Elements in LIFO orderStack<T>N/AConcurrentStack<T>ImmutableStack<T>
Elements sorted head—> tail or tail—> headLinkedList<T>N/ANoneN/A
Unique itemsHashSet<T>SortedSet<T>NoneImmutableHashSet<T>, ImmutableSortedSet<T>

Notes

  • A SortedDictionary<TKey,TValue> performs better than a SortedList<TKey,TValue> at the expense of more memory used.
  • For Queues and Stacks, elements are generally discarded after they are accessed.

Other Collections

System.Collections.Frozen

This namespace includes collection types that do not allow any changes to keys and values once a collection is created. They are more performant than than “unfrozen” types.

See https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen?view=net-9.0.

System.Collections.ObjectModel

Use these collections when properties or methods return collections:

CollectionDescription
Collection<T>Base class for a generic collection
KeyedCollection<TKey, TItem>Abstract base class for a collection whose keys are embedded in the values
ObservableCollection<T>Provides notifications when items get added or removed, or when the whole list is refreshed
ReadOnlyCollection<T>Base class for a generic read-only collection
ReadOnlyDictionary<TKey, TValue>A read-only, generic collection of key/value pairs
ReadOnlyObservableCollection<T>A read-only ObservableCollection

Others…

CollectionUse when you need…
BlockingCollection<T>a thread-safe collection that blocks Add/Take operations when full/empty
HybridDictionarya ListDictionary when collection is small; a Hashtable when it grows large
LinkedList<T>a collection with elements sorted head —> tail or tail —> head
NameValueCollectiona collection with one key, multiple values
OrderedDictionarya collection whose elements can be accessed by both key and index
OrderedDictionary<TKey, TValue>(.NET 9) a generic version of the above
PriorityQueue<TKey,TValue>a collection of elements and a priority
StringCollectiona list of only strings
StringDictionarya dictionary of only strings

Algorithmic Complexity

MutableAmortizedWorst CaseImmutableComplexity
Stack<T>.PushO(1)O(n)ImmutableStack<T>.PushO(1)
Queue<T>.EnqueueO(1)O(n)ImmutableQueue<T>.EnqueueO(1)
List<T>.AddO(1)O(n)ImmutableList<T>.AddO(logn)
List<T>.Item[Int32]O(1)O(1)ImmutableList<T>.Item[Int32]O(logn)
List<T>.EnumeratorO(n)O(n)ImmutableList<T>.EnumeratorO(n)
HashSet<T>.Add, lookupO(1)O(n)ImmutableHashSet<T>.AddO(logn)
SortedSet<T>.AddO(logn)O(n)ImmutableSortedSet<T>.AddO(logn)
Dictionary<T>.AddO(1)O(n)ImmutableDictionary<T>.AddO(logn)
Dictionary<T>lookupO(1)O(1) – or strictly O(n)ImmutableDictionary<T>lookupO(logn)
SortedDictionary<T>.AddO(logn)O(nlogn)ImmutableSortedDictionary<T>.AddO(logn)

Construction

var collection = new CollectionType<GenericType>();

or

var collection = new CollectionType<GenericType>{ item1, item2,  }

or

public class Galaxy 
{
    public string Name { get; set; }
    public int MegaLightYears { get; set; }
}

var theGalaxies = new CollectionType<Galaxy> 
{
    new Galaxy() { Name="Andromeda", MegaLightYears=3 },
    
}

Collection Initializers

Collection initializers let you specify one or more element initializers when you initialize a collection type that implements IEnumerable and has an Add method:

List<int> digits = new List<int> { 0, 1, 2, 3 };

Combining Collection Initializers with Object Initializers

List<Cat> cats = new List<Cat> 
{
    new Cat{ Name = "Silvester", Age = 2 };
    new Cat{ Name = "Tom", Age = 9 };
}

Iterating

for (var variable in collection) {  }

or

for (var index = 0; index < collection.Count; index++) {  }

Methods

Manipulating

collection.Add(element)
collection.Remove(element)
collection.RemoveAt(index)Elements after the one that are removed now have a lower index value.

Defining Custom Collections

It is usually better to use the .NET collections.
Implement either IEnumerable or IEnumerable<T>:

public class DaysOfTheWeek : IEnumerable 
{
    private string[] days = { "Sun", "Mon", "Tue",  }

    public IEnumerator GetEnumerator() 
    { // IEnumerable requires GetEnumerator() to be implemented.
        for (int index = 0; index < days.Length; index++) 
        {
            yield return days[index];
        }
    }
}