Page Content

Tutorials

What Is Mean By Collections In C# With Code Examples?

Collections in C#

Classes called collections are used in C# to store, organize, and retrieve sets of items. Compared to conventional arrays, they offer a wider variety of variable grouping options. There are many predefined collection classes available in the.NET Framework. It is also known as group.

Generic vs. Non-Generic Collections

C# provides collections in two primary categories:

Non-Generic Collections: Within the System.Namespace for collections. The universal basic type Object is used to store elements in these collections, thus any type can be added. Performance deterioration and a lack of compile-time type safety result from the requirement that value types be boxed (converted to Object) when added and unboxed when retrieved. In general, untyped collections are not recommended. The ArrayList class is one such instance.

Generic Collections: Found within the System.gatherings.general namespace. These are type-safe, so when you create an instance, you may specify what kinds of components they will keep. By doing away with the necessity for casting and boxing/unboxing, this enhances performance and detects type issues at compile time. It is strongly advised that modern C# programs use generic collections.

Common Generic Collection Types

The trio of fundamental collection types outlined in the Collections in System.Dictionary, List, and HashSet are examples of generic namespaces.

List

Characteristics: A generic version of ArrayList is List. By automatically resizing as elements are added or withdrawn, it works similarly to a dynamic array and allays worries about running out of space. Although they are not always organised, objects are kept in a certain order, and duplicates are allowed. It is fast to access specific elements through their index.

Common Operations:

Add(T item): Brings an item to the list’s conclusion.

RemoveAt(int index): Removes the element from a given location.

Insert(int index, T item): Adds a component at a specified location.

Contains(T item): Verifies whether the supplied element is present in the list. HashSet is a preferable option for frequent Contains tests since, despite its usefulness, List has O(n) performance.

Length/Count: Gives back the list’s element count.

Sort(): Arranges the list’s items.

Example

using System;
using System.Collections.Generic; // Required namespace for List<T>
namespace SimpleListExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. Declare and initialize a List of strings
            // The type String is specified within the angle brackets < >
            List<String> names = new List<String>(); 
            // 2. Add elements to the list
            Console.WriteLine("Enter three names:");
            for (int i = 0; i < 3; i++) //
            {
                string name = Console.ReadLine(); // Reads input from the console
                names.Add(name); // Adds the name to the end of the list 
            }
            Console.WriteLine("\nDisplaying all names in the list:");
            // 3. Iterate through the list using a foreach loop
            // The 'n' variable will be set to each element in turn
            foreach (String n in names)
            {
                Console.WriteLine(n);
            }
            // 4. Demonstrate searching for an element
            Console.WriteLine("\nPlease enter a name to search for:");
            String searchname = Console.ReadLine();
            if (names.Contains(searchname)) // Checks if the list contains the specified name
            {
                Console.WriteLine("Name found!");
            }
            else
            {
                Console.WriteLine("Name not found.");
            }
            Console.ReadLine(); // Keeps the console window open until a key is pressed
        }
    }
}

Output

Enter three names:
Geetha
Lavanya
Kowsalya
Displaying all names in the list:
Geetha
Lavanya
Kowsalya
Please enter a name to search for:
Geetha
Name found!

HashSet

Characteristics: HashSet is a mathematical set implementation. It does not follow a set order and instead stores unique items. Contains lookups are perfect for verifying membership because they are extremely quick, having O(1) speed.

Custom Types and Equality: In order to store custom objects in a HashSet (or as keys in a Dictionary), it is essential to override the Object class’s inherited Equals() and GetHashCode() methods. By doing this, you can make sure that the HashSet accurately detects duplicate items using your own concept of equality as opposed to merely object reference equality.

Common Operations: You can use the Add(), Clear(), Contains(), and Remove() methods. Sets are not ordered, so operations like Insert() and Sort() are not applicable. Additionally, HashSet offers set operations such as IsSubsetOf(), IsSupersetOf(), UnionWith(), and IntersectWith().

Example

using System;
using System.Collections.Generic; // Required namespace for HashSet<T>
using System.Linq; // Required for string.Join() in Console.WriteLine
namespace SimpleHashSetExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. Declare and initialize a HashSet of strings
            // This set will only store unique string values.
            Console.WriteLine("--- Initializing HashSets ---");
            HashSet<string> aspNetStudents = new HashSet<string>(); // 
            HashSet<string> silverlightStudents = new HashSet<string>(); // 
            // 2. Add elements to the first set
            Console.WriteLine("\nAdding students to ASP.NET set:");
            aspNetStudents.Add("S. Jobs"); // 
            aspNetStudents.Add("B. Gates"); // 
            aspNetStudents.Add("M. Dell"); // 
            aspNetStudents.Add("S. Jobs"); // Attempt to add a duplicate - it will not be added
            Console.WriteLine($"ASP.NET Students: {string.Join(", ", aspNetStudents)}");
            Console.WriteLine($"Number of ASP.NET Students: {aspNetStudents.Count}"); // 
            // 3. Add elements to the second set
            Console.WriteLine("\nAdding students to Silverlight set:");
            silverlightStudents.Add("M. Zuckerberg"); //
            silverlightStudents.Add("M. Dell"); // 
            silverlightStudents.Add("Bill Gates"); // Different from "B. Gates" intentionally
            Console.WriteLine($"Silverlight Students: {string.Join(", ", silverlightStudents)}");
            Console.WriteLine($"Number of Silverlight Students: {silverlightStudents.Count}");
            // 4. Demonstrate checking for existence (Contains method)
            Console.WriteLine("\n--- Checking for Student Existence ---");
            string searchName1 = "M. Dell";
            string searchName2 = "Elon Musk";
            Console.WriteLine($"Does ASP.NET set contain '{searchName1}'? {aspNetStudents.Contains(searchName1)}"); // 
            Console.WriteLine($"Does Silverlight set contain '{searchName2}'? {silverlightStudents.Contains(searchName2)}");
            // 5. Demonstrate Union (combining sets)
            Console.WriteLine("\n--- Demonstrating Set Union ---");
            HashSet<string> allStudents = new HashSet<string>();
            allStudents.UnionWith(aspNetStudents); // 
            allStudents.UnionWith(silverlightStudents); // 
            Console.WriteLine($"All Students (Union): {string.Join(", ", allStudents)}"); // "M. Dell" appears only once 
            // 6. Demonstrate Intersection (finding common elements)
            Console.WriteLine("\n--- Demonstrating Set Intersection ---");
            HashSet<string> commonStudents = new HashSet<string>(aspNetStudents); // Initialize with ASP.NET students
            commonStudents.IntersectWith(silverlightStudents); // 
            Console.WriteLine($"Students in both sets (Intersection): {string.Join(", ", commonStudents)}");
            // 7. Demonstrate Remove (removing elements)
            Console.WriteLine("\n--- Demonstrating Element Removal ---");
            Console.WriteLine($"Attempting to remove 'B. Gates' from ASP.NET set: {aspNetStudents.Remove("B. Gates")}"); // 
            Console.WriteLine($"ASP.NET Students after removal: {string.Join(", ", aspNetStudents)}");
            // 8. Demonstrate Clear (removing all elements)
            Console.WriteLine("\n--- Clearing a Set ---");
            silverlightStudents.Clear(); // [9, 25]
            Console.WriteLine($"Silverlight Students after clear: {string.Join(", ", silverlightStudents)}");
            Console.WriteLine($"Number of Silverlight Students after clear: {silverlightStudents.Count}");
            Console.ReadLine(); // Keeps the console window open
        }
    }
    // For custom types, you would typically define Equals and GetHashCode
    // For example:
    /*
    public class Student
    {
        public string Name { get; set; }
        public string DateOfBirth { get; set; }
        public Student(string name, string dob)
        {
            Name = name;
            DateOfBirth = dob;
        }
        // Must override Equals to define logical equality based on Name and DateOfBirth
        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
            {
                return false;
            }
            Student other = (Student)obj;
            return Name == other.Name && DateOfBirth == other.DateOfBirth;
        }
        // Must override GetHashCode to provide a consistent hash code for equal objects
        public override int GetHashCode()
        {
            // Simple way to combine hash codes of relevant fields 
            return (Name + DateOfBirth).GetHashCode();
        }
        public override string ToString()
        {
            return $"{Name} ({DateOfBirth})";
        }
    }
    */
}

Output

--- Initializing HashSets ---
Adding students to ASP.NET set:
ASP.NET Students: S. Jobs, B. Gates, M. Dell
Number of ASP.NET Students: 3
Adding students to Silverlight set:
Silverlight Students: M. Zuckerberg, M. Dell, Bill Gates
Number of Silverlight Students: 3
--- Checking for Student Existence ---
Does ASP.NET set contain 'M. Dell'? True
Does Silverlight set contain 'Elon Musk'? False
--- Demonstrating Set Union ---
All Students (Union): S. Jobs, B. Gates, M. Dell, M. Zuckerberg, Bill Gates
--- Demonstrating Set Intersection ---
Students in both sets (Intersection): M. Dell
--- Demonstrating Element Removal ---
Attempting to remove 'B. Gates' from ASP.NET set: True
ASP.NET Students after removal: S. Jobs, M. Dell
--- Clearing a Set ---
Silverlight Students after clear: 
Number of Silverlight Students after clear: 0

Dictionary<TKey, TValue>

Characteristics: Key-value pairs are used to store data in a dictionary (sometimes called an associative array or map). Although values can be replicated, each key must be distinct. Using their unique keys, dictionaries provide extremely effective lookups and value retrieval. Similar to HashSet, Dictionary\TKey, TValue> use hash tables to facilitate quick lookups, which leads to O(1) performance for the majority of operations.

SortedDictionary<K, V>: An alternate that uses a balanced tree (red-black tree) to maintain its elements’ internal key sorting. The sorting overhead may cause adding and removing elements to be slower, but obtaining elements by key is still effective and enables sorted iteration.

Common Operations: Using the [key] indexer to access items, Add(TKey key, TValue value), ContainsKey(), ContainsValue(), Remove(), and Clear().

Example

using System;
using System.Collections.Generic; // Required for Dictionary<TKey, TValue>
using System.Linq; // Required for string.Join() or LINQ methods like ToDictionary
namespace SimpleDictionaryExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. Declare and initialize a Dictionary mapping string (names) to int (ages) 
            Console.WriteLine("--- Initializing Dictionary ---");
            Dictionary<string, int> studentAges = new Dictionary<string, int>();
            // 2. Add elements to the dictionary
            Console.WriteLine("\nAdding students and their ages:");
            studentAges.Add("Alice", 20); // Add method
            studentAges.Add("Bob", 22);
            studentAges.Add("Charlie", 21);
            // Using indexer to add (if key doesn't exist) or update (if key exists)
            studentAges["David"] = 23;
            studentAges["Alice"] = 21; // Overwrites Alice's age from 20 to 21
            Console.WriteLine($"Current number of students: {studentAges.Count}"); // Count property 
            // 3. Attempt to add a duplicate key (will throw an ArgumentException) 
            Console.WriteLine("\nAttempting to add 'Bob' again:");
            try
            {
                studentAges.Add("Bob", 23); // This line will throw an ArgumentException
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
            // 4. Access elements using the key (indexer)
            Console.WriteLine("\nAccessing ages:");
            Console.WriteLine($"Alice's age: {studentAges["Alice"]}");
            // 5. Safely access elements using TryGetValue 
            string searchName1 = "Charlie";
            if (studentAges.TryGetValue(searchName1, out int charlieAge))
            {
                Console.WriteLine($"Found {searchName1}'s age: {charlieAge}");
            }
            else
            {
                Console.WriteLine($"Did not find {searchName1}'s age.");
            }
            string searchName2 = "Eve";
            if (studentAges.TryGetValue(searchName2, out int eveAge))
            {
                Console.WriteLine($"Found {searchName2}'s age: {eveAge}");
            }
            else
            {
                Console.WriteLine($"Did not find {searchName2}'s age.");
            }
            // 6. Check for key existence 
            Console.WriteLine($"\nDoes dictionary contain 'David'? {studentAges.ContainsKey("David")}");
            Console.WriteLine($"Does dictionary contain 'Frank'? {studentAges.ContainsKey("Frank")}");
            // 7. Iterate through the dictionary 
            Console.WriteLine("\nAll students and their ages:");
            foreach (KeyValuePair<string, int> student in studentAges) // KeyValuePair<K,V> is used for iteration [66-69]
            {
                Console.WriteLine($"Name: {student.Key}, Age: {student.Value}");
            }
            // You can also iterate through just keys or just values 
            Console.WriteLine("\nStudent Names:");
            foreach (string name in studentAges.Keys)
            {
                Console.WriteLine($"- {name}");
            }
            // 8. Remove an element
            Console.WriteLine("\nRemoving Bob:");
            bool removed = studentAges.Remove("Bob");
            Console.WriteLine($"Bob removed: {removed}");
            Console.WriteLine($"Current number of students: {studentAges.Count}");
            // 9. Clear the dictionary 
            Console.WriteLine("\nClearing the dictionary...");
            studentAges.Clear();
            Console.WriteLine($"Current number of students: {studentAges.Count}");
            Console.WriteLine($"Is dictionary empty? {studentAges.Count == 0}");
            Console.ReadLine(); // Keeps the console open
        }
    }
    // Example of a custom type to be used as a key, requiring Equals and GetHashCode overrides
    // (This part is not directly run in the Main method above but is crucial for custom keys)
    /*
    public class StudentKey
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public StudentKey(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }
        // Override Equals to define logical equality (two StudentKey objects are equal if their first and last names are the same) 
        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
            {
                return false;
            }
            StudentKey other = (StudentKey)obj;
            return FirstName == other.FirstName && LastName == other.LastName;
        }
        // Override GetHashCode to provide a consistent hash code for equal objects
        // This is vital for dictionary performance and correctness.
        public override int GetHashCode()
        {
            // A good practice is to combine hash codes of fields used in Equals.
            // Using a prime number multiplication and unchecked block for overflow is recommended.
            unchecked
            {
                int hash = 17; // A prime number
                hash = hash * 23 + (FirstName?.GetHashCode() ?? 0); // Combine hash code of FirstName
                hash = hash * 23 + (LastName?.GetHashCode() ?? 0);  // Combine hash code of LastName
                return hash;
            }
        }
        public override string ToString()
        {
            return $"{FirstName} {LastName}";
        }
    }
    */
}

Output

--- Initializing Dictionary ---
Adding students and their ages:
Current number of students: 4
Attempting to add 'Bob' again:
Error: An item with the same key has already been added. Key: Bob
Accessing ages:
Alice's age: 21
Found Charlie's age: 21
Did not find Eve's age.
Does dictionary contain 'David'? True
Does dictionary contain 'Frank'? False
All students and their ages:
Name: Alice, Age: 21
Name: Bob, Age: 22
Name: Charlie, Age: 21
Name: David, Age: 23
Student Names:
- Alice
- Bob
- Charlie
- David
Removing Bob:
Bob removed: True
Current number of students: 3
Clearing the dictionary...
Current number of students: 0
Is dictionary empty? True

Iterating Over Collections

For quick and simple iteration across collections and array-type data, C#’s foreach loop is a specialised iteration statement. It iterates across an array or collection of elements, running a block of code on each one. Foreach makes the iteration variable read-only. This indicates that any attempt by the embedded statement to change the iteration variable will result in a compile-time error. The IEnumerable interface and its GetEnumerator method are used in the foreach loop.

string[] names = { "Elvis", "Beatles", "Eagles", "Rolling Stones" }; 
foreach (string person in names) 
{
    Console.WriteLine(person); 
}

You can use the break keyword to end a foreach loop early or continue to move on to the next iteration.

Strings and Immutability

Remembering that strings in C# are immutable from our last discussion is crucial. In other words, any action that seems to change a string actually makes a new string object in memory. On the other hand, the majority of common collection types, such as List, HashSet, and Dictionary\TKey, TValue>, are mutable; this means that adding, deleting, or updating components can be done in-place without requiring the creation of a new collection object for every update. The StringBuilder class should be utilised for performance-critical applications that involve substantial string alterations since it offers a mutable buffer for text manipulation that functions similarly to mutable collections.

Serialization of Collections

It is simple to serialise collections such as List, HashSet, and Dictionary\TKey, TValue>. The process of serialisation involves transforming an object’s state into a format that can be transferred or stored and then later rebuilt. By adding the [Serialisable] keyword to the class declaration, you can make a class (and consequently its collections) serialisable. To save their whole state, the serialisation system automatically manages referenced items and builds a “object graph” of connected objects.

Code Example (Serialization):

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary; // 
// Mark classes as serializable
[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
    public override string ToString()
    {
        return $"Name: {Name}, Age: {Age}";
    }
}
[Serializable]
public class AddressBook
{
    public List<Person> Contacts { get; set; }
    public AddressBook()
    {
        Contacts = new List<Person>();
    }
    public void AddContact(Person person)
    {
        Contacts.Add(person);
    }
}
public class SerializationExample
{
    public static void Main(string[] args)
    {
        AddressBook myAddressBook = new AddressBook();
        myAddressBook.AddContact(new Person("John Doe", 30));
        myAddressBook.AddContact(new Person("Jane Smith", 25));
        // Serialize the AddressBook object 
        using (FileStream fs = new FileStream("AddressBook.dat", FileMode.Create))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(fs, myAddressBook);
            Console.WriteLine("Address book serialized successfully.");
        }
        // Deserialize the AddressBook object 
        AddressBook loadedAddressBook;
        using (FileStream fs = new FileStream("AddressBook.dat", FileMode.Open))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            loadedAddressBook = (AddressBook)formatter.Deserialize(fs);
            Console.WriteLine("\nAddress book deserialized successfully.");
        }
        Console.WriteLine("\nContacts from deserialized address book:");
        foreach (Person person in loadedAddressBook.Contacts)
        {
            Console.WriteLine(person);
        }
    }
}

Output

Address book serialized successfully.
Address book deserialized successfully.
Contacts from deserialized address book:
Name: John Doe, Age: 30
Name: Jane Smith, Age: 25

Development Tools Support

For dealing with C# collections, Integrated Development Environments (IDEs) such as Microsoft Visual Studio offer comprehensive support. For collection classes and associated methods, features like IntelliSense provide real-time documentation and code completion. Visualising class relationships, such as how collections like List or Dictionary are utilised as properties within other classes, is made possible via the Class Designer.

C# collections are essentially specialised containers, each with its own set of rules for accessing and organising objects. Similar to a flexible shopping list, a list allows you to add items in any order, even duplicates. A hash set functions similarly to a club membership roster, allowing only unique members regardless of their order of joining. Similar to a phone book, a dictionary helps you find a person’s phone number (the value) by using their unique name (the key).

You can also read C# Enumerations (Enums) Important Features and Declarations

Agarapu Geetha
Agarapu Geetha
My name is Agarapu Geetha, a B.Com graduate with a strong passion for technology and innovation. I work as a content writer at Govindhtech, where I dedicate myself to exploring and publishing the latest updates in the world of tech.
Index