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.
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().
// 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.
// 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.
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.
// 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.
// 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:
IEnumerable<T>/IEnumerable: For iteration.ICollection<T>/ICollection: For basic collection operations like count and adding/removing.IList<T>/IList: For indexed access and manipulation.IDictionary<TKey, TValue>/IDictionary: For key-value pair storage.
Best Practices
- Prefer Generic Collections: Always use generic collections (e.g.,
List<T>,Dictionary<TKey, TValue>) over their non-generic counterparts (ArrayList,Hashtable) for type safety and performance. - Choose the Right Collection: Select the collection type that best suits your needs. Use
List<T>for ordered sequences,Dictionary<TKey, TValue>for fast lookups by key,HashSet<T>for unique unordered elements, and so on. - Consider Performance: Be mindful of the time complexity of operations when choosing a collection. For instance, searching in a
List<T>can be slow for large lists (O(n)), while dictionary lookups are very fast (O(1) on average). - Use LINQ: Leverage Language Integrated Query (LINQ) to perform complex querying and manipulation operations on collections in a concise and readable manner.