In C# programming, a collection denotes a cluster of objects, offering a range of classes for organizing elements in a versatile manner. Collections are widely employed for managing, modifying, removing, fetching, exploring, and arranging objects. They provide a more organized and effective approach to dealing with multiple items compared to individual variables. These functionalities are integrated within the System.Collections, System.Collection.Generic, and System.Collections.Concurrent namespaces within the .NET Framework.
In C#, collections offer a range of functions such as automatic resizing, searching, sorting, and convenient data retrieval, making them highly beneficial for practical software development. Essentially, C# collections can handle all aspects of data structuring tasks effectively.
We have the option to store objects in either an array or a collection. A collection offers a distinct advantage compared to an array due to its flexibility. Unlike an array, which has a fixed size limit, a collection can dynamically expand or contract as objects are stored within it.
Types of Collections in C#
There exist primarily three methods to handle collections. The trio of namespaces for collections are as outlined below:
The CSS code snippet below illustrates the styling for a placeholder element:
.placeholder-diagram {
background: linear-gradient(135deg, #374151 0%, #1f2937 100%);
border-radius: 12px;
padding: 40px;
margin: 20px 0;
text-align: center;
}
.placeholder-diagram .placeholder-icon {
font-size: 3rem;
margin-bottom: 10px;
}
.placeholder-diagram .placeholder-text {
color: #9ca3af;
font-size: 1rem;
}
Here, we will examine these groupings individually.
System.Collections.Generic Classes
In C#, the Generic Collection is specified within the System.Collection.Generic namespace. These collections provide a generic approach to common data structures like stacks, queues, linked lists, dictionaries, and various others.
Generic collections are considered type-safe due to their generic nature, ensuring that they can solely contain collections compatible with the specified type. This characteristic eliminates inadvertent type conflicts, thereby eliminating the need for boxing and unboxing. Consequently, this enhances the overall performance, flexibility, and efficiency of the software.
The System.Collections.Generic namespace contains numerous classes in C#. A few examples are:
| Classes | Description |
|---|---|
| List_PRESERVE8__ | It is a generic list that keeps elements in a sequence that has an order and supports access by index. It can automatically increase or decrease in size, has duplicate elements, and offers helpful methods, such as Add, Remove, and Sort. |
| Stack_PRESERVE9__ | It is used to represent a LIFO collection in which elements are added with Push and removed with Pop. The Peek provides a view of the top element without its removal, which is helpful for applications such as undo operations. |
| Queue_PRESERVE10__ | It is used to represent an instance based on the First-In-First-Out (FIFO) concept, where an item is added at the tail by Enqueue and an item is taken from the head by Dequeue. Peek is applicable to look at the front element without removing it, which is an apt application for scheduling tasks. |
| LinkedList_PRESERVE11__ | It is used to represent a doubly linked list in which every element holds references to the next and previous node. It is optimal for constant insertions and removals towards both ends or in the middle, and contains methods such as AddFirst, AddLast, and Remove. |
| HashSet_PRESERVE12__ | It is used to represent a data structure that only contains unique elements and no duplicates. It is hashing-based, so search and lookups are extremely quick, but the elements' order is not kept. It also supports set operations, such as Union and Intersection. |
| SortedSet_PRESERVE13__ | It is comparable to HashSet, but maintains elements in sorted order. It enforces uniqueness and supports efficient search while being particularly useful when dealing with sorted data ranges. |
| Dictionary_PRESERVE14__ | It is used to represent a list of key-value pairs, with each key requiring uniqueness. It supports fast lookups and value retrieval using keys and includes methods such as Add, Remove, and TryGetValue for safe access. |
| SortedDictionary_PRESERVE15__ | This is a function like a Dictionary, but keeps its elements sorted by key. It is useful where fast lookups are required along with sorted traversal of the key-value pairs. |
| SortedList_PRESERVE16__ | It is used to store the elements in sorted order by key. It takes less memory than SortedDictionary and grants access by both key and index, although insertion and deletion can be slower for bigger collections. |
C# System.Collections.Generic classes Example
Let's consider an example to demonstrate the classes within System.Collections.Generic namespace in C#.
Example
using System;
using System.Collections.Generic;
class C# Tutorial
{
static void Main()
{
// using List<T> function
List<int> numbers = new List<int>() { 5, 10, 15 };
numbers.Add(20);
Console.WriteLine("List: " + string.Join(", ", numbers));
// Using Dictionary<TKey, TValue> function
Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Brendon"] = 27;
ages["James"] = 32;
Console.WriteLine("Dictionary:");
foreach (var kvp in ages)
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
// Using Queue<T> function
Queue<string> queue = new Queue<string>();
queue.Enqueue("First");
queue.Enqueue("Second");
Console.WriteLine("Queue Dequeue: " + queue.Dequeue());
// using Stack<T> function
Stack<string> stack = new Stack<string>();
stack.Push("Bottom");
stack.Push("Top");
Console.WriteLine("Stack Pop: " + stack.Pop());
// using LinkedList<T> function
LinkedList<int> linkedList = new LinkedList<int>();
linkedList.AddLast(14);
linkedList.AddLast(26);
linkedList.AddFirst(13);
Console.WriteLine("LinkedList: ");
foreach (var item in linkedList)
Console.Write(item + " ");
Console.WriteLine();
}
}
Output:
List: 5, 10, 15, 20
Dictionary:
Brendon: 27
James: 32
Queue Dequeue: First
Stack Pop: Top
LinkedList:
13 14 26
Explanation:
In this example, we use various generic collection classes from the System.Collections.Generic namespace. First, it displays a List<T> for dynamic arrays, a Dictionary<TKey, TValue> for key-value pairs, Queue<T> for FIFO operations, Stack<T> for LIFO operations, and LinkedList<T> for a doubly linked list, which performs basic operations like adding, removing, and iterating elements.
System.Collections classes
In C# programming, the System.Collections namespace provides non-generic collection classes. These classes serve as versatile data structures that manipulate object references. They have the capability to store objects of any type and are not type-specific. Non-generic collections are constructed using a combination of interfaces and classes.
The System.Collections namespace contains numerous classes in C#. A few examples are:
| Classes | Description |
|---|---|
| ArrayList | ArrayList holds elements of any class in a dynamic indexed list and can increase or decrease in size as necessary. However, it is not type-safe and is primarily utilized for backward compatibility. |
| Stack | Stack is a last-in, first-out (LIFO) data structure, which pushes items in with the Push() function, pops them out with the Pop() function, and accesses the latest one first, which is better for reversing orders or dealing with recursive calls. |
| Queue | A queue is a FIFO collection, including insert with Enqueue(), remove with Dequeue(), items accessed in order of entry, with applications in scheduling and buffer operations. |
| Hashtable | It is used to store key-value pairs for quick lookups, where each key has to be unique, and values may be any object, which makes it effective for mapping and associative storage. |
C# System.Collections Classes Example
Let's consider a scenario to demonstrate the System.Collections classes in C#.
Example
using System;
using System.Collections;
class C# Tutorial
{
static void Main()
{
// using ArrayList function
ArrayList arrayList = new ArrayList() { 1, "Hello C# Tutorial", 3.14 };
arrayList.Add(100);
Console.WriteLine("ArrayList: ");
foreach (var item in arrayList)
Console.Write(item + " ");
Console.WriteLine("\n");
// using Hashtable function
Hashtable hashtable = new Hashtable();
hashtable["id"] = 101;
hashtable["name"] = "Jhonson";
Console.WriteLine("Hashtable: ");
foreach (DictionaryEntry entry in hashtable)
Console.WriteLine($"{entry.Key}: {entry.Value}");
Console.WriteLine();
// using Queue function
Queue queue = new Queue();
queue.Enqueue("First");
queue.Enqueue("Second");
Console.WriteLine("Queue Dequeue: " + queue.Dequeue());
Console.WriteLine();
// using Stack function
Stack stack = new Stack();
stack.Push("Bottom");
stack.Push("Top");
Console.WriteLine("Stack Pop: " + stack.Pop());
Console.WriteLine();
}
}
Output:
ArrayList:
1 Hello C# Tutorial 3.14 100
Hashtable:
id: 101
name: Jhonson
Queue Dequeue: First
Stack Pop: Top
Explanation:
In this instance, we illustrate the utilization of specialized collection classes from the System.Collections namespace. Initially, it illustrates how ArrayList dynamically stores elements of various types, Hashtable organizes key-value pairs, Queue operates on a first-in, first-out basis, and Stack follows the last-in, first-out principle. Subsequently, it executes basic add, remove, and iteration tasks.
System.Collections.Concurrent Classes
In C#, the System.Collections.Concurrent namespace contains classes that support operations safe for use across multiple threads. This namespace was introduced starting from .NET Framework 4 and was specifically tailored for applications with multiple threads. The collections within this namespace allow for concurrent addition, removal, and modification of data without the need for explicit locks, offering a more efficient and secure alternative to utilizing System.Collections or System.Collections.Generic.
The System.Collections.Concurrent namespace in C# contains a variety of classes. A few examples include:
| Classes | Description |
|---|---|
| BlockingCollection | It offers a thread-safe collection with blocking and bounding features for producers and consumers to wait if the collection is empty or full, which is applicable in producer-consumer situations. |
| ConcurrentBag | It is a thread-safe, unordered collection and is most efficient when many threads are inserting and deleting items continuously, but the order does not matter. |
| ConcurrentStack | It is a thread-safe LIFO-based stack and is appropriate for use in undo actions or backtracking in a multithreading environment. |
| ConcurrentQueue | It is a first-in, first-out (FIFO) thread-safe data structure suitable for processing jobs or tasks in sequence by many threads simultaneously. |
| ConcurrentDirectory | It is a thread-safe key-value pair dictionary for storing and supporting concurrent write and read operations, which enable efficient multi-threading access to dictionary values. |
| Partitioner | It helps to split a data source into multiple partitions in order to distribute work equally between threads in parallel processing and improve performance in data-parallel operations. |
| OrderablePartitioner | It extends Partitioner with the added functionality to maintain element order while partitioning, useful when parallel jobs process ordered data. |
C# System.Collections.Concurrent Example
Let's consider an illustration to demonstrate the System.Collections.Concurrent namespace in C#.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class C# Tutorial
{
static void Main()
{
var dict = new ConcurrentDictionary<int, string>();
dict.TryAdd(1, "One");
dict[2] = "Two";
dict.AddOrUpdate(2, "Second", (key, oldValue) => oldValue + " Updated");
if (dict.TryGetValue(1, out string value))
{
Console.WriteLine($"Key 1 = {value}");
}
Parallel.For(3, 6, i =>
{
dict[i] = $"Number {i}";
});
Console.WriteLine("Final dictionary:");
foreach (var a in dict)
{
Console.WriteLine($"{a.Key} : {a.Value}");
}
}
}
Output:
Key 1 = One
Final dictionary:
1 : One
2 : Two Updated
3 : Number 3
4 : Number 4
5 : Number 5
Explanation:
In this instance, we showcase the thread-safe functionality of ConcurrentDictionary. Within this demonstration, we execute various tasks such as adding elements, updating values, searching for keys, and inserting multiple entries. Ultimately, it showcases the complete collection of key-value pairs within the dictionary.
General Categories of Collections
C# collections are divided into four primary groups: Indexed Collections, Key-Value Pair Collections, Prioritized Collections, and Specialized Collections.
The design below illustrates a placeholder with specific styling properties:
.placeholder-diagram { background: linear-gradient(135deg, #374151 0%, #1f2937 100%); border-radius: 12px; padding: 40px; margin: 20px 0; text-align: center; }
.placeholder-diagram .placeholder-icon { font-size: 3rem; margin-bottom: 10px; }
.placeholder-diagram .placeholder-text { color: #9ca3af; font-size: 1rem; }
Here, we will explore each of these collection categories individually.
Indexed-Based Collections
Indexed collections store elements that are retrieved using an internal index position. Examples of such collections include Array, List, and ArrayList. Each item's location is determined by its index, allowing for efficient access and modification.
Key-Value Pair Collections
Key-value pair collections store data in pairs to enable easy access based on keys. This category encompasses various classes like Hashtable, Dictionary, and SortedList. These collections are essential when data needs to be accessed or modified using a specific key rather than its position.
Prioritized Collection
Prioritized collections organize elements based on a specific sequence or importance, such as Queue, Stack, and SortedList, determining the manner in which items are managed.
Specialized Collections
Specialized data structures are tailored for specific usage scenarios, such as ensuring thread safety or accommodating unique architectures. Instances include LinkedList along with the collections available in System.Collections.Concurrent, among others designed for parallel processing or specific operational requirements.
Features of C# Collections
There are several features of C# collections. Some of them are as follows:
- In C#, collections can grow or shrink in size during runtime.
- Generic collections offer compile-time type checking, which helps to reduce runtime errors.
- Collections can store data as lists, queues, stacks, dictionaries, sets, and linked lists, depending on the requirement.
- In C#, System.Collections.Concurrent offer built-in thread-safe classes for parallel and multi-threaded programming.
- It can be optimized for several operations, including sorting, searching, inserting, and deleting.
- Collections can support non-generic, generic, and thread-safe concurrent collections.
Conclusion
In summary, collections in C# refer to a cluster of elements that aid in the efficient management and manipulation of related data items collectively. The ability to adjust size dynamically and maintain type safety guarantees adaptable, sustainable, and reliable code. Additionally, the inclusion of concurrency support and diverse built-in functions solidifies the significance of collections in developing scalable contemporary C# applications.
C# Collections FAQs
The primary distinction between collections and arrays in C# lies in their flexibility and functionality. While arrays have a fixed size and type, collections can dynamically grow in size and store different types of elements.
The primary contrast between collections and arrays lies in the fact that arrays have a fixed size and enforce type safety. In contrast, collections in C# are flexible in size and can be adjusted during program execution.
2) Are collections in C# resizable?
Yes, most dynamic collections, including List<T>, Queue<T>, and Stack<T>, automatically increase their size as needed.
The primary contrast between List<T> and ArrayList in C# lies in their underlying implementations and capabilities.
The primary contrast between List<T> and ArrayList lies in List<T> being type-safe and being part of the generic collections, guaranteeing compile-time type verification. In contrast, ArrayList stores objects as type Object, necessitating casting and resulting in reduced efficiency and type safety.
4) Are collections thread-safe in C#?
In C#, the core collections do not inherently support thread safety. This implies that concurrent read and write operations by multiple threads on a collection can lead to issues like race conditions, data inconsistencies, and errors.
5) Can collections store duplicate values in C#?
Yes, most C# collections, such as List<T> and ArrayList, allow storing duplicate values. On the other hand, HashSet<T> and keys in Dictionary<TKey, TValue> must be unique.