Skip to content

C# - Collections

Collections are classes designed to store, manage, and manipulate groups of objects. C# provides a rich set of collection classes in the System.Collections.Generic namespace, which are essential for modern application development.

Modern C# development exclusively uses generic collections (e.g., List<T>, Dictionary<TKey, TValue>). The <T> signifies a type parameter, allowing you to create collections that are strongly typed for a specific data type (like List<int> for integers or List<string> for strings).

Benefits of Generic Collections:

  • Type Safety: The compiler enforces that only the correct type of item can be added, preventing runtime errors.
  • Performance: They avoid the performance overhead of “boxing” and “unboxing” that occurs with non-generic collections when working with value types (like int or struct).
  • IntelliSense Support: Because the type is known, your code editor provides better autocompletion and error checking.

A Note on Legacy Collections: Older C# code might use non-generic collections from the System.Collections namespace, such as ArrayList and Hashtable. These are considered legacy and should not be used in new code due to their lack of type safety and lower performance.

Here are the most commonly used generic collections:

A List<T> is the modern, type-safe equivalent of ArrayList. It represents a strongly typed list of objects that can be accessed by index. Its size grows dynamically as you add elements.

using System;
using System.Collections.Generic;
// Create a list of strings
var fruits = new List<string> { "Apple", "Banana", "Cherry" };
// Add an element
fruits.Add("Date");
// Access by index
Console.WriteLine($"The first fruit is: {fruits[0]}"); // Output: The first fruit is: Apple
// Iterate over the list
foreach (var fruit in fruits)
{
Console.WriteLine(fruit);
}

2. Dictionary<TKey, TValue> - The Key-Value Store

Section titled “2. Dictionary<TKey, TValue> - The Key-Value Store”

A Dictionary<TKey, TValue> stores key-value pairs, offering extremely fast lookups based on the key. It is the modern replacement for Hashtable.

using System;
using System.Collections.Generic;
var userAges = new Dictionary<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};
// Add a new entry
userAges["Charlie"] = 35;
// Retrieve a value by key
if (userAges.TryGetValue("Alice", out int age))
{
Console.WriteLine($"Alice's age is: {age}"); // Output: Alice's age is: 30
}
// Check for existence
if (userAges.ContainsKey("David"))
{
Console.WriteLine("David exists.");
} else {
Console.WriteLine("David does not exist.");
}

A HashSet<T> contains a collection of unique elements. It is highly optimized for checking if an item is present in the set.

using System;
using System.Collections.Generic;
var uniqueTags = new HashSet<string> { "csharp", "dotnet", "programming" };
// Adding a duplicate item returns false and doesn't change the set
bool added = uniqueTags.Add("csharp"); // returns false
Console.WriteLine($"Added duplicate 'csharp': {added}");
// Fast containment check
if (uniqueTags.Contains("dotnet"))
{
Console.WriteLine("The set contains 'dotnet'.");
}
// Output:
// Added duplicate 'csharp': False
// The set contains 'dotnet'.

A Queue<T> is a “First-In, First-Out” collection, like a line at a store. A Stack<T> is a “Last-In, First-Out” collection, like a stack of plates.

// Queue Example (FIFO)
var taskQueue = new Queue<string>();
taskQueue.Enqueue("Task 1");
taskQueue.Enqueue("Task 2");
Console.WriteLine(taskQueue.Dequeue()); // Output: Task 1
// Stack Example (LIFO)
var browserHistory = new Stack<string>();
browserHistory.Push("Page 1");
browserHistory.Push("Page 2");
Console.WriteLine(browserHistory.Pop()); // Output: Page 2

Language-Integrated Query (LINQ) provides powerful and readable syntax for filtering, sorting, and transforming data in any collection that implements IEnumerable<T>.

using System.Collections.Generic;
using System.Linq;
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Use LINQ to find all even numbers greater than 5
var results = numbers
.Where(n => n % 2 == 0) // Filtering
.Where(n => n > 5); // Chaining another filter
Console.WriteLine("Even numbers greater than 5:");
foreach (var number in results)
{
Console.WriteLine(number);
}
// Output:
// Even numbers greater than 5:
// 6
// 8
// 10