Generic COLLECTIONS part 1
This backpack is special because it can hold all sorts of things, like toys, books, or even snacks! You can put things in, take them out, and organize them in different ways. In coding we call it a Collection. Let's understand them one by one.
List<T>
A List<T>
in C# is a dynamic array, meaning its size can grow or shrink as needed. It's a versatile collection that allows you to store elements of a specific type in a sequential order.
Key Features:
Dynamic Resizing: The list automatically adjusts its capacity to accommodate new elements.
Zero-Based Indexing: Elements are accessed using integer indices, starting from 0.
Duplicate Elements: Lists can contain duplicate elements.
Efficient Random Access: Elements can be accessed directly by their index, making it efficient for random access.
Various Operations: Lists support various operations like adding, removing, inserting, searching, sorting, and reversing elements.
Common Operations on List<T>
1. Creating a List:
C#
List<int> numbers = new List<int>(); // Create an empty list of integers
2. Adding Elements:
C#
numbers.Add(10); // Add an element to the end of the list
numbers.Insert(1, 20); // Insert an element at index 1
3. Accessing Elements:
C#
int firstNumber = numbers[0]; // Access the first element
4. Removing Elements:
C#
numbers.Remove(20); // Remove the first occurrence of 20
numbers.RemoveAt(1); // Remove the element at index 1
5. Finding Elements:
C#
int index = numbers.IndexOf(30); // Find the index of 30
bool contains40 = numbers.Contains(40); // Check if 40 exists
6. Sorting Elements:
C#
numbers.Sort(); // Sort the list in ascending order
numbers.Reverse(); // Reverse the order of elements
7. Iterating Over Elements:
C#
foreach (int number in numbers)
{
Console.WriteLine(number);
}
Underlying Implementation
A List<T>
is typically implemented using an array. When you add elements to a list, the underlying array is resized to accommodate the new element. This resizing process can be expensive in terms of performance, especially if the list grows rapidly. To mitigate this, the list often allocates more space than it immediately needs, anticipating future growth.
When to Use List<T>
When you need to store a collection of elements in a specific order.
When you need to access elements by index.
When you need to dynamically add or remove elements.
When you need to perform various operations like sorting, searching, and reversing.
By understanding the key features and operations of List<T>
, you can effectively use it to manage and manipulate data in your C# applications.
Dictionary<TKey, TValue>
A Dictionary<TKey, TValue>
in C# is a collection of key-value pairs. It's like a real-world dictionary, where each word (key) has a corresponding definition (value).
Key Features:
Unique Keys: Each key in the dictionary must be unique.
Efficient Lookups: You can quickly retrieve a value by specifying its key.
Dynamic Size: The dictionary can grow or shrink as needed.
No Specific Order: Elements are not stored in a specific order.
Common Operations on Dictionary<TKey, TValue>:
1. Creating a Dictionary:
Dictionary<string, int> ages = new Dictionary<string, int>();
ages.Add("Alice", 25); // Adds a key-value pair
int aliceAge = ages["Alice"]; // Access the value associated with the key "Alice"
ages.Remove("Bob"); // Removes the key-value pair with the key "Bob"
//Checking for Key Existence:
bool containsCharlie = ages.ContainsKey("Charlie");
// Iterating Over Key-Value Pairs:
foreach (KeyValuePair<string, int> entry in ages)
{
Console.WriteLine(entry.Key + ": " + entry.Value);
}
Underlying Implementation:
A Dictionary<TKey, TValue>
often uses a hash table to store key-value pairs. A hash table is a data structure that allows for efficient lookups by hashing the key to determine its location within the table.
When to Use Dictionary<TKey, TValue>:
When you need to store data associated with unique keys.
When you need to quickly retrieve values based on keys.
When you don't need to maintain a specific order of elements.
By understanding the key features and operations of Dictionary<TKey, TValue>
, you can effectively use it to store and retrieve data in your C# applications.
HashSet<T>
A HashSet<T>
is a collection of unique elements. Think of it as a set in mathematics, where each element can only appear once.
Key Features:
Unique Elements: No duplicates are allowed.
Unordered: Elements are not stored in a specific order.
Efficient Membership Testing: Quickly determine if an element exists in the set.
Hash-Based Implementation: Uses a hash table for efficient operations.
Common Operations:
Adding Elements:
C#
HashSet<int> numbers = new HashSet<int>(); numbers.Add(10); numbers.Add(20); // Adding 10 again will be ignored as it's already present // Checking for Membership: bool contains20 = numbers.Contains(20); // Returns true //Removing Elements: numbers.Remove(10); //Iterating Over Elements: foreach (int number in numbers) { Console.WriteLine(number); }
When to Use HashSet<T>:
When you need to store a collection of unique elements.
When you need to quickly check if an element exists in the collection.
When you don't need to maintain a specific order of elements.
Example:
Imagine you're building a program to track unique website visitors. You can use a HashSet<string>
to store the unique IP addresses of visitors. As new visitors come, you can add their IP address to the set. If the IP address already exists, it won't be added again, ensuring you only count unique visitors.
SortedSet<T>: A Deep Dive
A SortedSet<T>
in C# is a collection of unique elements that are automatically sorted in ascending order. It's a powerful tool for maintaining a sorted list of elements without duplicates.
Key Features:
Unique Elements: No duplicate elements are allowed.
Sorted Order: Elements are automatically sorted in ascending order based on their natural comparison or a custom comparer.
Efficient Operations: It provides efficient operations for adding, removing, searching, and iterating over elements.
Tree-Based Implementation: It's typically implemented using a self-balancing binary search tree, which ensures efficient logarithmic time complexity for most operations.
Common Operations:
Creating a SortedSet:
SortedSet<int> numbers = new SortedSet<int>(); //Adding Elements: numbers.Add(10); numbers.Add(20); numbers.Add(5); // Will be inserted in the correct sorted position //Checking for Membership: bool contains20 = numbers.Contains(20); //Removing Elements: numbers.Remove(10); //Iterating Over Elements: foreach (int number in numbers) { Console.WriteLine(number); // Elements will be printed in sorted order } //Finding Minimum and Maximum Elements: int minNumber = numbers.Min; int maxNumber = numbers.Max;
Custom Sorting:
You can customize the sorting behavior of a SortedSet<T>
by providing a custom IComparer<T>
implementation. This allows you to sort elements based on specific criteria other than their natural comparison.
class Person
{
public string Name { get; set; }
public int Age { get; set; }
// ... other properties and methods
}
// Custom comparer to sort by age in descending order
class AgeDescendingComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
return y.Age.CompareTo(x.Age);
}
}
SortedSet<Person> people = new SortedSet<Person>(new AgeDescendingComparer());
people.Add(new Person { Name = "Alice", Age = 25 });
people.Add(new Person { Name = "Bob", Age = 30 });
people.Add(new Person { Name = "Charlie", Age = 20 });
// People will be sorted by age in descending order
When to Use SortedSet<T>:
When you need to store a collection of unique elements in sorted order.
When you need to efficiently find the minimum or maximum element.
When you want to iterate over elements in sorted order.
When you need to customize the sorting behavior using a custom comparer.
By understanding the key features and operations of SortedSet<T>
, you can effectively use it to manage and manipulate sorted data in your C# applications.
Feature | List<T> ๐ | Dictionary<TKey, TValue> ๐ | HashSet<T> ๐ฒ | SortedSet<T> ๐ |
Order | Ordered | Unordered | Unordered | Sorted |
Duplicate Elements | Allowed | Keys must be unique, values can be duplicated | Not allowed | Not allowed |
Efficient Operations | Random access, adding/removing at any index | Efficient lookup by key | Efficient membership testing, adding/removing | Efficient sorted operations, finding min/max |
Common Use Cases | Storing a sequence of elements, implementing stacks/queues | Storing key-value pairs, lookup by key | Storing unique elements, checking membership | Storing unique elements in sorted order, finding min/max |
Special Powers | Flexible, can be used for many purposes | Efficient lookup by key | Efficient membership testing, removes duplicates | Efficient sorted operations, unique elements |
Underlying Data Structure | Array | Hash table | Hash table | Self-balancing binary search tree |
Memory Usage | Can be inefficient for large lists due to resizing | Efficient for large datasets | Efficient for large datasets | More memory overhead due to tree structure |
Best Use Cases | When you need to store a sequence of elements and access them by index | When you need to store key-value pairs and quickly retrieve values by key | When you need to store unique elements and check for membership efficiently | When you need to store unique elements in sorted order and perform efficient range queries |