C# Collections, Sorting, Generics, and Data Structures

Why Use Collections?

Arrays have fixed sizes—once allocated, their length cannot change. This rigidity leads to either wasted memory (if oversized) or the need for code changes when requirements evolve. Collections, in contrast, dynamically resize as elements are added or removed.

Generic List<T>

List<T> is a strongly typed collection from the System.Collections.Generic namespace. The type parameter T enforces that all elements must be of the same type, eliminating the need for casting upon retrieval.

Common operations include:

  • Add(T item) – appends an element
  • Remove(T item) – removes the first matching element
  • Count – gets the number of elements
  • Iteration via foreach

Its primary advantage is compile-time type safety.

Dictionary<K, V>

Dictionary<K, V> stores key-value pairs with O(1) average lookup time. Like List<T>, it is generic, avoiding boxing/unboxing and ensuring type safety at compile time. Keys must be unique; attempting to add a duplicate key throws an exception.

Default Sorting with List<T>

For primitive types (e.g., int, string), list.Sort() works out of the box. For custom types, the class must implement IComparable<T>:

public int CompareTo(Student other)
{
    return this.StuId.CompareTo(other.StuId); // ascending by ID
}

This approach supports only one default sort order per type.

Dynamic Sorting with IComparer<T>

When multiple sort criteria are needed (e.g., sort by name ascending or age descending), implement IComparer<T> in separate classes:

class AgeAscending : IComparer<Student>
{
    public int Compare(Student x, Student y) => x.Age.CompareTo(y.Age);
}

class NameDescending : IComparer<Student>
{
    public int Compare(Student x, Student y) => y.StuName.CompareTo(x.StuName);
}

Pass an instance to Sort():

list.Sort(new AgeAscending());

This replaces the default IComparable<T> behavior entirely.

LINQ-Based Sorting

LINQ provides a fluent syntax for sorting without modifying the original list:

var byAgeDesc = list.OrderByDescending(s => s.Age).ToList();
var byNameAsc = list.OrderBy(s => s.StuName).ToList();

These methods return new lists and support chaining (e.g., ThenBy).

Generics Deep Dive

Generics defer type specification until instantiation, enabling reusable, type-safe code. Supported constructs include generic classes, methods, interfaces, and delegates.

Constraints

Use where clauses to restrict allowable types:

  • where T : class – reference type
  • where T : struct – value type
  • where T : new() – must have a parameterless constructor
  • where T : BaseClass, IInterface – multiple constraints (must be compatible)

Constraints grant access to members of the constrained type while enforcing correctness.

Covariance and Contravariance

  • Covariance (out): Allows assignment from more derived to less derived types in output positions (e.g., IEnumerable<Sparrow>IEnumerable<Bird>).
  • Contravariance (in): Permits assignment from less derived to more derived types in input positions (e.g., Action<Bird>Action<Sparrow>).

These features enible flexible generic interface usage while preserving type safety.

Generic Caching

Static generic types (e.g., static class Cache<T>) maintain separate state per T, offering high-performance caching without manual management—but memory persists for the application lifetime.

Core Data Structures

Arrays vs. Lists

Type Element Type Size Boxing? Notes
T[] Homogeneous Fixed No Fast indexing
ArrayList Heterogeneous Dynamic Yes Obsolete; use List<T>
List<T> Homogeneous Dynamic No Preferred dynamic array

Linked Structures

  • LinkedList<T>: Doubly linked; efficient insert/delete at ends, no indexing.
  • Queue<T>: FIFO; Enqueue, Dequeue, Peek.
  • Stack<T>: LIFO; Push, Pop, Peek.

Thread-safe variants: ConcurrentQueue<T>, ConcurrentStack<T>.

Sets

  • HashSet<T>: Unordered, unique elements; supports set operations (union, intersect, etc.).
  • SortedSet<T>: Ordered, unique elements; uses IComparer<T> for sorting.

Key-Value Stores

Type Ordering Thread-Safe? Notes
Hashtable No Yes* Non-generic; legacy
Dictionary<K,V> Insertion No Preferred generic map
SortedDictionary Key No Tree-based; slower inserts
SortedList Key No Array-backed; memory-efficient

ConcurrentDictionary<K,V> provides thread-safe operations.

Key Interfaces

  • IEnumerable<T>: Enables foreach; base for all collections.
  • ICollection<T>: Adds Count, Add, Remove, etc.
  • IList<T>: Supports indexing and positional operations.
  • IQueryable<T>: Represents query expressions (e.g., for databases); execution deferred until enumeration.

Tags: C# Collections generics Sorting Data Structures

Posted on Tue, 28 Jul 2026 16:32:46 +0000 by Nymphetamine