C# Collections

Storing and managing groups of data efficiently

📦 What are Collections?

Collections are data structures that store groups of related objects. Unlike arrays, collections can grow and shrink dynamically, making them flexible for managing data in your applications.


// Simple List collection example
using System.Collections.Generic;

List<string> fruits = new List<string>();
fruits.Add("Apple");
fruits.Add("Banana");
Console.WriteLine(fruits[0]); // Apple
                                    

Output:

Apple

Common Collection Types

📋

List<T>

Dynamic array with index access

List<int> numbers = 
new List<int>();
🗂️

Dictionary<K,V>

Key-value pairs for fast lookup

Dictionary<string, int> 
ages = new();
🎯

HashSet<T>

Unique items, no duplicates

HashSet<string> unique = 
new HashSet<string>();
📚

Queue<T>

First-In-First-Out (FIFO)

Queue<string> line = 
new Queue<string>();

🔹 List<T> - Dynamic Arrays

The List<T> collection in C# functions as a dynamic, type-safe array that automatically resizes. It provides methods like Add(), Remove(), Find(), and Sort(). Unlike arrays, Lists grow as needed, making them perfect for scenarios where the number of elements is unknown. They are the go-to choice for ordered, modifiable collections of objects.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Create a list of strings
        List<string> cities = new List<string>();
        
        // Add items
        cities.Add("New York");
        cities.Add("London");
        cities.Add("Tokyo");
        
        // Access by index
        Console.WriteLine("First city: " + cities[0]);
        
        // Count items
        Console.WriteLine("Total cities: " + cities.Count);
        
        // Loop through list
        foreach (string city in cities)
        {
            Console.WriteLine("- " + city);
        }
    }
}

Output:

First city: New York

Total cities: 3

- New York

- London

- Tokyo

🔹 Dictionary<TKey, TValue> - Key-Value Pairs

A Dictionary<TKey, TValue> stores data as unique key-value pairs, enabling near-instant O(1) lookups by key. Each key must be unique, making it perfect for associations like ID-to-object mappings, configuration settings, or caching. It provides methods for adding, removing, and checking for keys. Dictionaries are fundamental for efficient data retrieval in applications like in-memory databases, lookup tables, or routing mechanisms, where fast access by a unique identifier is critical for performance.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Create a dictionary
        Dictionary<string, int> ages = new Dictionary<string, int>();
        
        // Add key-value pairs
        ages["Alice"] = 25;
        ages["Bob"] = 30;
        ages["Charlie"] = 35;
        
        // Access by key
        Console.WriteLine("Alice's age: " + ages["Alice"]);
        
        // Check if key exists
        if (ages.ContainsKey("Bob"))
        {
            Console.WriteLine("Bob is in the dictionary");
        }
        
        // Loop through dictionary
        foreach (var pair in ages)
        {
            Console.WriteLine(pair.Key + " is " + pair.Value + " years old");
        }
    }
}

Output:

Alice's age: 25

Bob is in the dictionary

Alice is 25 years old

Bob is 30 years old

Charlie is 35 years old

🔹 HashSet<T> - Unique Collections

HashSet<T> is a high-performance collection designed to store unique elements, automatically preventing duplicates. It uses hash-based lookups for fast membership tests (Contains), adds, and removals. This makes it ideal for scenarios requiring a distinct set of items, such as removing duplicates from a list, tracking visited nodes in a graph, or managing a collection of tags. HashSet<T> supports set operations like union, intersection, and difference, enabling powerful data comparisons and aggregations.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Create a HashSet
        HashSet<string> colors = new HashSet<string>();
        
        // Add items
        colors.Add("Red");
        colors.Add("Blue");
        colors.Add("Red"); // Duplicate - won't be added
        
        Console.WriteLine("Total unique colors: " + colors.Count);
        
        // Check if item exists
        if (colors.Contains("Blue"))
        {
            Console.WriteLine("Blue is in the set");
        }
        
        // Display all items
        foreach (string color in colors)
        {
            Console.WriteLine("- " + color);
        }
    }
}

Output:

Total unique colors: 2

Blue is in the set

- Red

- Blue

🔹 Queue<T> and Stack<T>

Queue<T> and Stack<T> are specialized collections for processing items in specific orders. A Queue follows First-In-First-Out (FIFO), perfect for task scheduling, message buffering, or breadth-first search. A Stack follows Last-In-First-Out (LIFO), ideal for undo/redo functionality, parsing expressions, or depth-first search. Both provide efficient enqueue/dequeue and push/pop operations, making them essential for algorithms and scenarios where order of processing is a fundamental requirement.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Queue - FIFO
        Queue<string> line = new Queue<string>();
        line.Enqueue("First person");
        line.Enqueue("Second person");
        line.Enqueue("Third person");
        
        Console.WriteLine("Queue (FIFO):");
        Console.WriteLine("Serving: " + line.Dequeue()); // First person
        
        // Stack - LIFO
        Stack<string> plates = new Stack<string>();
        plates.Push("Plate 1");
        plates.Push("Plate 2");
        plates.Push("Plate 3");
        
        Console.WriteLine("\nStack (LIFO):");
        Console.WriteLine("Taking: " + plates.Pop()); // Plate 3
    }
}

Output:

Queue (FIFO):

Serving: First person

Stack (LIFO):

Taking: Plate 3

🔹 Collection Initialization

C# provides a concise syntax for initializing collections inline, improving code readability and reducing verbosity. Using curly braces, you can populate List<T>, Dictionary<TKey, TValue>, and other collections at the point of creation. For example: new List<int> { 1, 2, 3 } or new Dictionary<string, int> { {"A", 1} }. This feature saves multiple Add calls, makes intent clearer, and is widely used in setting up test data, configuration, or default collections.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // List initialization
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
        
        // Dictionary initialization
        Dictionary<string, string> capitals = new Dictionary<string, string>
        {
            { "USA", "Washington DC" },
            { "France", "Paris" },
            { "Japan", "Tokyo" }
        };
        
        // Display
        Console.WriteLine("Numbers: " + string.Join(", ", numbers));
        Console.WriteLine("Capital of France: " + capitals["France"]);
    }
}

Output:

Numbers: 1, 2, 3, 4, 5

Capital of France: Paris

🧠 Test Your Knowledge

Which collection automatically prevents duplicate values?