C# > Advanced C# > Collections and Generics > List<T>, Dictionary<TKey, TValue>, HashSet<T>

Working with Collections: List, Dictionary, and HashSet

This example demonstrates how to use List, Dictionary, and HashSet in C# to manage collections of data efficiently. We'll explore adding, removing, searching, and iterating through elements in each collection type. This provides a foundational understanding of when and how to leverage each collection effectively.

Introduction to Collections

C# provides several collection classes for storing and manipulating groups of objects. List, Dictionary, and HashSet are three of the most commonly used and offer distinct advantages depending on the application's needs. Understanding their differences is crucial for writing performant and maintainable code.

List: Ordered Collection

List represents a strongly typed list of objects that can be accessed by index. Elements can be added or removed at any position. This makes it suitable for scenarios where the order of elements is important and you need to access elements by their position. The example demonstrates adding elements, accessing elements by index, iterating, checking for existence, and removing elements.

using System;
using System.Collections.Generic;

public class ListExample
{
    public static void Main(string[] args)
    {
        // Create a list of strings
        List<string> names = new List<string>();

        // Add elements to the list
        names.Add("Alice");
        names.Add("Bob");
        names.Add("Charlie");

        // Access elements by index
        Console.WriteLine("First name: " + names[0]);

        // Iterate through the list
        Console.WriteLine("All names:");
        foreach (string name in names)
        {
            Console.WriteLine(name);
        }

        // Check if an element exists
        bool containsBob = names.Contains("Bob");
        Console.WriteLine("Contains Bob: " + containsBob);

        // Remove an element
        names.Remove("Bob");
        Console.WriteLine("List after removing Bob:");
        foreach (string name in names)
        {
            Console.WriteLine(name);
        }
    }
}

Dictionary: Key-Value Pairs

Dictionary stores key-value pairs where each key is unique. It provides fast lookups based on the key. This is ideal for scenarios where you need to quickly retrieve a value associated with a specific key. The example demonstrates adding key-value pairs, accessing values by key, checking for key existence, iterating, and removing key-value pairs.

using System;
using System.Collections.Generic;

public class DictionaryExample
{
    public static void Main(string[] args)
    {
        // Create a dictionary with string keys and integer values
        Dictionary<string, int> ages = new Dictionary<string, int>();

        // Add key-value pairs
        ages.Add("Alice", 30);
        ages.Add("Bob", 25);
        ages.Add("Charlie", 35);

        // Access a value by its key
        Console.WriteLine("Alice's age: " + ages["Alice"]);

        // Check if a key exists
        bool hasBob = ages.ContainsKey("Bob");
        Console.WriteLine("Contains Bob: " + hasBob);

        // Iterate through the dictionary
        Console.WriteLine("All ages:");
        foreach (KeyValuePair<string, int> pair in ages)
        {
            Console.WriteLine(pair.Key + ": " + pair.Value);
        }

        // Remove a key-value pair
        ages.Remove("Bob");
        Console.WriteLine("Dictionary after removing Bob:");
        foreach (KeyValuePair<string, int> pair in ages)
        {
            Console.WriteLine(pair.Key + ": " + pair.Value);
        }
    }
}

HashSet: Unique Values

HashSet is a collection that contains no duplicate elements. It provides fast lookups to check if an element exists. This is useful for scenarios where you need to ensure that all elements are unique and perform quick membership tests. The example demonstrates adding elements (including a duplicate), iterating, checking for existence, and removing elements. Notice that adding "Alice" twice only results in one instance in the set.

using System;
using System.Collections.Generic;

public class HashSetExample
{
    public static void Main(string[] args)
    {
        // Create a hash set of strings
        HashSet<string> uniqueNames = new HashSet<string>();

        // Add elements to the hash set
        uniqueNames.Add("Alice");
        uniqueNames.Add("Bob");
        uniqueNames.Add("Charlie");
        uniqueNames.Add("Alice"); // Adding a duplicate

        // Iterate through the hash set
        Console.WriteLine("Unique names:");
        foreach (string name in uniqueNames)
        {
            Console.WriteLine(name);
        }

        // Check if an element exists
        bool containsBob = uniqueNames.Contains("Bob");
        Console.WriteLine("Contains Bob: " + containsBob);

        // Remove an element
        uniqueNames.Remove("Bob");
        Console.WriteLine("Hash set after removing Bob:");
        foreach (string name in uniqueNames)
        {
            Console.WriteLine(name);
        }
    }
}

Real-Life Use Case: Inventory Management

Imagine an inventory management system. A List could store the order in which items were received. A Dictionary could map product IDs to their quantities. A HashSet could store a set of unique product categories.

Best Practices

  • Choose the appropriate collection type based on your specific needs.
  • Consider performance implications when selecting a collection. For example, Dictionary provides faster lookups than List for key-based searches.
  • Use descriptive variable names to improve code readability.
  • Handle potential exceptions when accessing elements, especially with dictionaries where accessing a non-existent key throws an exception. Use TryGetValue for safer access.

Interview Tip

Be prepared to explain the differences between List, Dictionary, and HashSet, and provide scenarios where each would be the most suitable choice. Understanding the time complexity of common operations (add, remove, search) for each collection type is also important.

When to Use Them

  • List: Use when you need an ordered collection and access by index is important.
  • Dictionary: Use when you need to store key-value pairs and require fast lookups based on the key.
  • HashSet: Use when you need to store a collection of unique elements and require fast membership tests.

Memory Footprint

The memory footprint varies. Lists generally use more memory than arrays because they allow dynamic resizing. Dictionaries have some overhead due to hashing and internal data structures. HashSets also have some overhead because they need to ensure uniqueness.

Alternatives

  • Arrays: Offer lower overhead than List, but have fixed size.
  • SortedDictionary: A dictionary that maintains keys in sorted order.
  • SortedSet: A set that maintains elements in sorted order.

Pros

  • List: Dynamic size, easy to add/remove elements.
  • Dictionary: Fast key-based lookups.
  • HashSet: Ensures uniqueness, fast membership tests.

Cons

  • List: Slower lookups than Dictionary for specific elements.
  • Dictionary: Requires unique keys, higher memory overhead.
  • HashSet: No ordering of elements.

FAQ

  • What happens if I add a duplicate key to a Dictionary?

    Adding a duplicate key to a Dictionary will throw an ArgumentException. You should check if the key exists using ContainsKey before adding a new key-value pair or use the indexer to update the existing value. Alternatively, consider using TryAdd to avoid exceptions.
  • How do I iterate through a Dictionary to access both keys and values?

    You can iterate through a Dictionary using a foreach loop and access the keys and values through the KeyValuePair object. Alternatively, you can use dictionary.Keys to iterate through keys only, or dictionary.Values to iterate through values only.
  • What is the time complexity of checking if an element exists in a HashSet?

    The time complexity of checking if an element exists in a HashSet is, on average, O(1) (constant time), assuming a good hash function and minimal collisions. In the worst-case scenario (e.g., all elements hash to the same bucket), it can degrade to O(n), where n is the number of elements in the set.