In C#, collections are used to manage groups of related objects. There are three main namespaces for working with collections, each designed for different needs:
- System.Collections.Generic
- System.Collections
- System.Collections.Concurrent
1) System.Collections.Generic Classes
The System.Collections.Generic
namespace provides strongly-typed collections that offer better performance and type safety. The classes in this namespace include:
List
A dynamic array that can grow as needed. Provides methods to search, sort, and manipulate lists.
Example
using System;
using System.Collections.Generic;
List<int> numbers = new List<int> { 1, 2, 3 };
numbers.Add(4);
Console.WriteLine(string.Join(", ", numbers)); // Output: 1, 2, 3, 4
Stack
A last-in, first-out (LIFO) collection of objects. Useful for reversing the order of elements.
Example
using System.Collections.Generic;
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
Console.WriteLine(stack.Pop()); // Output: 2
Queue
A first-in, first-out (FIFO) collection of objects. Useful for storing elements in the order they were added.
Example
using System.Collections.Generic;
Queue<int> queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(2);
Console.WriteLine(queue.Dequeue()); // Output: 1
LinkedList
A doubly linked list. Each node contains a reference to the next and previous nodes, allowing efficient insertions and deletions.
Example
using System.Collections.Generic;
LinkedList<int> linkedList = new LinkedList<int>();
linkedList.AddLast(1);
linkedList.AddLast(2);
Console.WriteLine(linkedList.First.Value); // Output: 1
HashSet
A collection of unique elements. Useful for storing distinct elements and performing set operations.
Example
using System.Collections.Generic;
HashSet<int> set = new HashSet<int> { 1, 2, 3 };
set.Add(2); // Duplicate, will not be added
Console.WriteLine(set.Count); // Output: 3
SortedSet
A collection of unique elements sorted in order. Useful for storing sorted distinct elements.
Example
using System.Collections.Generic;
SortedSet<int> sortedSet = new SortedSet<int> { 3, 1, 2 };
Console.WriteLine(string.Join(", ", sortedSet)); // Output: 1, 2, 3
Dictionary<TKey, TValue>
A collection of key/value pairs. Provides fast lookups by key.
Example
using System.Collections.Generic;
Dictionary<int, string> dict = new Dictionary<int, string>
{
{ 1, "One" },
{ 2, "Two" }
};
Console.WriteLine(dict[1]); // Output: One
SortedDictionary<TKey, TValue>
A collection of key/value pairs sorted by key. Combines the functionality of a dictionary with sorted keys.
Example
using System.Collections.Generic;
SortedDictionary<int, string> sortedDict = new SortedDictionary<int, string>
{
{ 2, "Two" },
{ 1, "One" }
};
Console.WriteLine(string.Join(", ", sortedDict)); // Output: [1, One], [2, Two]
SortedList<TKey, TValue>
A collection of key/value pairs sorted by key, implemented as a list. Provides efficient access by index and key.
Example
using System.Collections.Generic;
SortedList<int, string> sortedList = new SortedList<int, string>
{
{ 2, "Two" },
{ 1, "One" }
};
Console.WriteLine(sortedList[0]); // Output: [1, One]
2) System.Collections Classes
These classes are considered legacy and are not recommended for new development. They are not strongly typed and thus less efficient and more error-prone compared to System.Collections.Generic
classes. The System.Collections
namespace includes:
ArrayList
A dynamic array that can hold objects of any type. It is not type-safe.
Example
using System;
using System.Collections;
ArrayList arrayList = new ArrayList { 1, "Two", 3.0 };
arrayList.Add(4);
Console.WriteLine(arrayList[1]); // Output: Two
Stack
A non-generic LIFO collection. Similar to Stack<T>
but not type-safe.
Example
using System.Collections;
Stack stack = new Stack();
stack.Push(1);
stack.Push("Two");
Console.WriteLine(stack.Pop()); // Output: Two
Queue
A non-generic FIFO collection. Similar to Queue<T>
but not type-safe.
Example
using System.Collections;
Queue queue = new Queue();
queue.Enqueue(1);
queue.Enqueue("Two");
Console.WriteLine(queue.Dequeue()); // Output: 1
Hashtable
A collection of key/value pairs. Similar to Dictionary<TKey, TValue>
but not type-safe.
Example
using System.Collections;
Hashtable hashtable = new Hashtable
{
{ 1, "One" },
{ "Two", 2 }
};
Console.WriteLine(hashtable[1]); // Output: One
3) System.Collections.Concurrent Classes
The System.Collections.Concurrent
namespace provides thread-safe collections. These classes are designed for use in multi-threaded environments and help prevent issues that arise from concurrent access.
BlockingCollection
A thread-safe collection that supports adding and taking items. Useful for producer-consumer scenarios.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
BlockingCollection<int> collection = new BlockingCollection<int>();
Task.Run(() =>
{
for (int i = 0; i < 5; i++)
{
collection.Add(i);
Console.WriteLine($"Added: {i}");
}
collection.CompleteAdding();
});
Task.Run(() =>
{
foreach (var item in collection.GetConsumingEnumerable())
{
Console.WriteLine($"Consumed: {item}");
}
}).Wait();
ConcurrentBag
A thread-safe, unordered collection of objects. Useful for storing objects accessed by multiple threads.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
ConcurrentBag<int> bag = new ConcurrentBag<int>();
Parallel.For(0, 10, i =>
{
bag.Add(i);
});
foreach (var item in bag)
{
Console.WriteLine(item);
}
ConcurrentStack
A thread-safe LIFO collection. Similar to Stack<T>
but designed for concurrent access.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
ConcurrentStack<int> stack = new ConcurrentStack<int>();
Parallel.For(0, 10, i =>
{
stack.Push(i);
});
int result;
while (stack.TryPop(out result))
{
Console.WriteLine(result);
}
ConcurrentQueue
A thread-safe FIFO collection. Similar to Queue<T>
but designed for concurrent access.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
Parallel.For(0, 10, i =>
{
queue.Enqueue(i);
});
int result;
while (queue.TryDequeue(out result))
{
Console.WriteLine(result);
}
ConcurrentDictionary<TKey, TValue>
A thread-safe collection of key/value pairs. Similar to Dictionary<TKey, TValue>
but designed for concurrent access.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
ConcurrentDictionary<int, string> dict = new ConcurrentDictionary<int, string>();
Parallel.For(0, 10, i =>
{
dict.TryAdd(i, $"Value{i}");
});
foreach (var kvp in dict)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
Partitioner
Provides a way to partition a collection into smaller parts for parallel processing.
OrderablePartitioner
Similar to Partitioner
, but also preserves the ordering of elements in the collection.
Conclusion
C# offers a rich set of collection classes across three namespaces to suit various needs:
System.Collections.Generic
for strongly-typed, high-performance collections.System.Collections
for legacy collections.System.Collections.Concurrent
for thread-safe collections in multi-threaded environments.
By understanding and using these collections effectively, you can write more efficient, readable, and maintainable C# code.