Sets, queues, and stacks are fundamental data structures that offer unique ways to organize and manage data. Unlike lists and dictionaries, which you've already explored, these collections provide specific rules for adding and removing elements, making them suitable for particular programming tasks. Understanding these data structures will expand your ability to solve problems efficiently and write cleaner, more maintainable code.

Sets: Ensuring Uniqueness

A set is a collection that contains no duplicate elements. This characteristic makes sets useful for tasks like removing duplicates from a list, checking for membership, and performing mathematical set operations.

Key Characteristics of Sets

Common Set Operations

Example: Using HashSet<T>

C# provides the HashSet<T> class to implement sets. Here's an example:

using System;
using System.Collections.Generic;

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

        // Add some names, including duplicates
        uniqueNames.Add("Alice");
        uniqueNames.Add("Bob");
        uniqueNames.Add("Charlie");
        uniqueNames.Add("Alice"); // Duplicate, will be ignored

        // Print the unique names
        Console.WriteLine("Unique names:");
        foreach (string name in uniqueNames)
        {
            Console.WriteLine(name);
        }

        // Check if a name exists
        bool containsBob = uniqueNames.Contains("Bob");
        Console.WriteLine($"\nContains Bob: {containsBob}"); // Output: True

        // Remove a name
        uniqueNames.Remove("Bob");

        // Check again
        containsBob = uniqueNames.Contains("Bob");
        Console.WriteLine($"Contains Bob after removal: {containsBob}"); // Output: False

        // Demonstrate set operations
        HashSet<string> otherNames = new HashSet<string>() { "Charlie", "David", "Eve" };

        Console.WriteLine("\nOriginal uniqueNames:");
        foreach (string name in uniqueNames)
        {
            Console.WriteLine(name);
        }

        Console.WriteLine("\nOther names:");
        foreach (string name in otherNames)
        {
            Console.WriteLine(name);
        }

        uniqueNames.UnionWith(otherNames);
        Console.WriteLine("\nAfter UnionWith:");
        foreach (string name in uniqueNames)
        {
            Console.WriteLine(name);
        }

        HashSet<string> set1 = new HashSet<string>() { "Alice", "Bob", "Charlie" };
        HashSet<string> set2 = new HashSet<string>() { "Bob", "David", "Eve" };

        set1.IntersectWith(set2);
        Console.WriteLine("\nAfter IntersectWith:");
        foreach (string name in set1)
        {
            Console.WriteLine(name);
        }

        HashSet<string> set3 = new HashSet<string>() { "Alice", "Bob", "Charlie" };
        HashSet<string> set4 = new HashSet<string>() { "Bob", "David", "Eve" };

        set3.ExceptWith(set4);
        Console.WriteLine("\nAfter ExceptWith:");
        foreach (string name in set3)
        {
            Console.WriteLine(name);
        }

        HashSet<string> set5 = new HashSet<string>() { "Alice", "Bob", "Charlie" };
        HashSet<string> set6 = new HashSet<string>() { "Bob", "David", "Eve" };

        set5.SymmetricExceptWith(set6);
        Console.WriteLine("\nAfter SymmetricExceptWith:");
        foreach (string name in set5)
        {
            Console.WriteLine(name);
        }
    }
}

In this example: