C# Collections Types

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:

  1. System.Collections.Generic
  2. System.Collections
  3. 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.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top