C# Collections

Introduction to C# Collections

Collections in C# are fundamental data structures that allow you to store and manage groups of objects. They provide a flexible and efficient way to handle data compared to using plain arrays. The .NET Framework offers a rich set of collection classes, primarily found in the System.Collections and System.Collections.Generic namespaces.

Using collections simplifies common programming tasks such as adding, removing, searching, and iterating over data. Understanding the different types of collections and when to use them is crucial for writing efficient and maintainable C# code.

Generic Collections

Generic collections, introduced in C# 2.0 and residing in the System.Collections.Generic namespace, are strongly typed. This means they are parameterized with a type (e.g., List<int>, Dictionary<string, Customer>), which eliminates the need for casting and prevents runtime type errors. Generic collections offer better performance and type safety.

The List<T> Class

List<T> is one of the most commonly used generic collection types. It represents a strongly typed list of objects that can be accessed by index. It dynamically resizes as elements are added or removed.

Adding Elements

You can add elements to a List<T> using the Add() method.

Example: Adding integers to a list

using System.Collections.Generic;

// Create a new list of integers
List<int> numbers = new List<int>();

// Add elements
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);

// You can also add multiple elements using AddRange
numbers.AddRange(new int[] { 40, 50 });
                

Removing Elements

Elements can be removed by their value using Remove() or by their index using RemoveAt().

Example: Removing elements from a list

// Assuming 'numbers' list from the previous example

// Remove by value
numbers.Remove(20); // Removes the first occurrence of 20

// Remove by index (e.g., remove the element at index 0)
numbers.RemoveAt(0);
                

Accessing Elements

Elements are accessed using their zero-based index.

Example: Accessing list elements

// Assuming 'numbers' list contains { 30, 40, 50 }

int firstElement = numbers[0]; // firstElement will be 30
int secondElement = numbers[1]; // secondElement will be 40

// You can also iterate through the list
foreach (int number in numbers)
{
    Console.WriteLine(number);
}
                

The Dictionary<TKey, TValue> Class

Dictionary<TKey, TValue> stores key-value pairs. Each key must be unique, and it provides fast lookups of values based on their associated keys.

Adding Key-Value Pairs

Use the indexer or the Add() method to insert pairs.

Example: Adding items to a dictionary

using System.Collections.Generic;

// Create a dictionary mapping strings to integers
Dictionary<string, int> studentScores = new Dictionary<string, int>();

// Add elements using indexer
studentScores["Alice"] = 95;
studentScores["Bob"] = 88;

// Add elements using Add() method
studentScores.Add("Charlie", 76);
                

Looking Up Values

Retrieve values using their keys via the indexer. Use TryGetValue() for safe access without throwing exceptions if the key doesn't exist.

Example: Looking up values in a dictionary

// Assuming 'studentScores' dictionary from previous example

int aliceScore = studentScores["Alice"]; // aliceScore will be 95

// Safe lookup
if (studentScores.TryGetValue("Bob", out int bobScore))
{
    Console.WriteLine($"Bob's score: {bobScore}"); // Prints "Bob's score: 88"
}
else
{
    Console.WriteLine("Bob not found.");
}

if (!studentScores.ContainsKey("David"))
{
    Console.WriteLine("David is not in the dictionary.");
}
                

Accessing Keys and Values

You can get collections of all keys or all values.

Example: Accessing keys and values

// Assuming 'studentScores' dictionary

// Get all keys
IEnumerable<string> studentNames = studentScores.Keys;

// Get all values
IEnumerable<int> scores = studentScores.Values;

// Iterate through key-value pairs
foreach (KeyValuePair<string, int> entry in studentScores)
{
    Console.WriteLine($"{entry.Key}: {entry.Value}");
}
                

The Hashtable Class (Non-Generic)

Hashtable is an older, non-generic collection that stores key-value pairs. It stores all elements as objects, requiring casting and potentially leading to runtime errors. It is generally recommended to use the generic Dictionary<TKey, TValue> instead.

The ArrayList Class (Non-Generic)

ArrayList is a non-generic, dynamic array. Like Hashtable, it stores elements as objects, necessitating casting and offering less type safety and performance than List<T>. Use List<T> for type-safe and efficient list operations.

Common Collection Interfaces

Several interfaces define contracts for collection behavior, allowing for polymorphism and interoperability:

Best Practices