# Mastering C# Part 5.2 Collections

### **Array**

An **Array** is a fixed-size collection of elements of the same type. It is part of the `System` namespace. Arrays are useful when you know the number of elements in advance and do not require resizing.

---

### **Key Features of an Array**

1. **Fixed Size**: Size is determined at creation and cannot be changed.
    
2. **Type-Safe**: All elements must be of the same type.
    
3. **Fast Access**: Directly access elements using an index.
    
4. **Best Use Case**: When the number of elements is fixed or known beforehand.
    

---

### **Example of an Array**

```csharp
using System;

class Program
{
    static void Main()
    {
        // Declare and initialize an array
        int[] numbers = new int[5] { 1, 2, 3, 4, 5 };

        // Access and modify elements
        Console.WriteLine($"First element: {numbers[0]}"); // Output: 1
        numbers[0] = 10;

        // Iterate through the array
        Console.WriteLine("\\nArray Elements:");
        foreach (var num in numbers)
        {
            Console.WriteLine(num);
        }
    }
}

```

**Output:**

```csharp
First element: 1

Array Elements:
10
2
3
4
5

```

---

### **ArrayList**

An **ArrayList** is a dynamically resizable, non-generic collection in the `System.Collections` namespace. It can store elements of **different types**. However, since it is non-generic, **type safety** is not enforced.

---

### **Key Features of an ArrayList**

1. **Dynamic Resizing**: Automatically grows as new elements are added.
    
2. **Type-Unsafe**: Can store objects of any type, leading to potential runtime errors.
    
3. **Slower than Array**: Performance is slightly slower because of boxing/unboxing for value types.
    
4. **Best Use Case**: When you need a dynamic collection and are using .NET versions older than 2.0 (otherwise, prefer `List<T>`).
    

---

### **Example of an ArrayList**

```csharp
using System;
using System.Collections;

class Program
{
    static void Main()
    {
        // Create an ArrayList
        ArrayList list = new ArrayList();

        // Add elements of different types
        list.Add(1);        // int
        list.Add("Two");    // string
        list.Add(3.0);      // double

        // Access elements
        Console.WriteLine($"First element: {list[0]}"); // Output: 1

        // Iterate through the ArrayList
        Console.WriteLine("\\nArrayList Elements:");
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }

        // Remove an element
        list.Remove("Two");
        Console.WriteLine("\\nAfter Removal:");
        foreach (var item in list)
        {
            Console.WriteLine(item);
        }
    }
}

```

**Output:**

```csharp
First element: 1

ArrayList Elements:
1
Two
3

After Removal:
1
3

```

---

### **Comparison of Array vs ArrayList**

| Feature | Array | ArrayList |
| --- | --- | --- |
| **Type-Safe** | Yes, only one type of elements | No, stores elements as `object` |
| **Resizable** | No, fixed size | Yes, dynamic resizing |
| **Performance** | Faster | Slower due to boxing/unboxing |
| **Namespace** | `System` | `System.Collections` |
| **Best Alternative** | Fixed data collection | Use `List<T>` for type safety |

---

### **When to Use Which?**

1. **Use** `Array`:
    
    * When the size is fixed.
        
    * When type safety and performance are critical.
        
2. **Use** `ArrayList`:
    
    * When the size of the collection changes frequently.
        
    * If working in older .NET versions (prefer `List<T>` in newer versions).
        

For modern applications, `List<T>` is usually preferred over `ArrayList`.

### HashTable

The `Hashtable` class in C# is part of the `System.Collections` namespace and represents a collection of key-value pairs. It is **non-generic**, meaning it can store keys and values of any data type, and provides fast lookup, insertion, and deletion operations. However, since it's not type-safe, it's less commonly used in modern C# applications where the `Dictionary<TKey, TValue>` class (which is generic) is preferred for better performance and type safety.

### **Key Features of** `Hashtable`

1. **Non-Generic**: Stores objects of any type (not type-safe).
    
2. **Key-Value Pairs**: It stores data as key-value pairs, where the key is unique.
    
3. **Hashing**: Uses a hash function to determine the index of elements for fast access.
    
4. **Thread-Safety**: Not thread-safe by default. For thread-safety, use `Hashtable.Synchronized`.
    
5. **Best Use Case**: When you need to store key-value pairs but don't care about type safety (though `Dictionary<TKey, TValue>` is a better alternative).
    

### **Basic Operations with** `Hashtable`

* **Add**: Adds a key-value pair to the Hashtable.
    
* **ContainsKey**: Checks if a particular key exists.
    
* **Remove**: Removes a key-value pair.
    
* **Indexer**: Allows access to values based on keys.
    

### **Example of** `Hashtable`

```csharp
using System;
using System.Collections;

class Program
{
    static void Main()
    {
        // Create a Hashtable
        Hashtable ht = new Hashtable();

        // Add key-value pairs to the Hashtable
        ht.Add(1, "Apple");
        ht.Add(2, "Banana");
        ht.Add(3, "Cherry");

        // Access values using keys
        Console.WriteLine($"Key 1: {ht[1]}"); // Output: Apple

        // Check if a key exists
        Console.WriteLine($"Contains key 2: {ht.ContainsKey(2)}"); // Output: True

        // Remove a key-value pair
        ht.Remove(2);

        // Iterate through the Hashtable
        Console.WriteLine("\\nHashtable Contents:");
        foreach (DictionaryEntry entry in ht)
        {
            Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
        }
    }
}

```

**Output:**

```csharp
Key 1: Apple
Contains key 2: True

Hashtable Contents:
Key: 1, Value: Apple
Key: 3, Value: Cherry

```

---

### **Important Methods and Properties**

1. **Add(key, value)**: Adds a key-value pair.
    
2. **ContainsKey(key)**: Returns `true` if the key exists.
    
3. **ContainsValue(value)**: Returns `true` if the value exists.
    
4. **Remove(key)**: Removes the entry with the specified key.
    
5. **Clear()**: Removes all key-value pairs from the Hashtable.
    
6. **Count**: Returns the number of key-value pairs in the Hashtable.
    
7. **Item\[key\]**: Allows access to the value associated with the specified key.
    

---

### **When to Use** `Hashtable`

1. **Legacy Code**: If you are maintaining or working with older code that uses `Hashtable`.
    
2. **Type Safety Not Important**: When type safety is not required and flexibility with key and value types is needed.
    
3. **Thread-Safety**: If you need thread-safe operations, you can use `Hashtable.Synchronized`.
    

However, for modern applications, `Dictionary<TKey, TValue>` is usually the better choice because it's **type-safe**, **faster**, and more flexible.

---

### `Hashtable` vs `Dictionary<TKey, TValue>`

| Feature | `Hashtable` | `Dictionary<TKey, TValue>` |
| --- | --- | --- |
| **Type-Safe** | No | Yes |
| **Performance** | Slightly slower (due to boxing/unboxing) | Faster (type-safe) |
| **Thread-Safety** | Not thread-safe by default, can use `Synchronized` | Not thread-safe by default, can use `ConcurrentDictionary` |
| **Generic Support** | No | Yes |
| **Use Case** | Legacy code, untyped collections | Modern, type-safe collections |

---

### **Conclusion**

* **Use** `Hashtable` when working with older code or when you need flexibility with different data types (but keep in mind it's not type-safe).
    
* **Use** `Dictionary<TKey, TValue>` for modern, type-safe, and high-performance code.
    

### **Hashtable Example with More Operations**

```csharp
using System;
using System.Collections;

class Program
{
    static void Main()
    {
        // Create a new Hashtable instance
        Hashtable ht = new Hashtable();

        // Add some key-value pairs
        ht.Add("1", "Apple");
        ht.Add("2", "Banana");
        ht.Add("3", "Cherry");
        ht.Add("4", "Date");
        ht.Add("5", "Elderberry");

        // Display the count of items in the Hashtable
        Console.WriteLine($"Hashtable contains {ht.Count} items.");

        // Accessing an element using a key (via the indexer)
        Console.WriteLine($"Element with key '3': {ht["3"]}");

        // Checking if a specific key exists in the Hashtable
        Console.WriteLine($"Contains key '2': {ht.ContainsKey("2")}");

        // Checking if a specific value exists in the Hashtable
        Console.WriteLine($"Contains value 'Banana': {ht.ContainsValue("Banana")}");

        // Removing an element by key
        ht.Remove("4");
        Console.WriteLine("\\nRemoved key '4'. Now the Hashtable contains:");

        // Iterating over the Hashtable (using DictionaryEntry)
        foreach (DictionaryEntry entry in ht)
        {
            Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
        }

        // Accessing a value that doesn't exist (throws KeyNotFoundException)
        try
        {
            Console.WriteLine($"Element with key '10': {ht["10"]}"); // Key doesn't exist
        }
        catch (KeyNotFoundException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }

        // Using the indexer to update an existing value
        ht["1"] = "Avocado";
        Console.WriteLine("\\nUpdated element with key '1':");
        Console.WriteLine($"Key: 1, New Value: {ht["1"]}");

        // Clear the Hashtable
        ht.Clear();
        Console.WriteLine($"\\nHashtable cleared. Now it contains {ht.Count} items.");
    }
}

```

### **Explanation of Operations in the Code**

1. **Creating the Hashtable**:
    
    ```csharp
    Hashtable ht = new Hashtable();
    
    ```
    
    This initializes an empty `Hashtable`.
    
2. **Adding Elements**:
    
    ```csharp
    ht.Add("1", "Apple");
    ht.Add("2", "Banana");
    
    ```
    
    Adds key-value pairs to the Hashtable.
    
3. **Accessing Elements**:
    
    ```csharp
    Console.WriteLine($"Element with key '3': {ht["3"]}");
    
    ```
    
    Retrieves the value associated with the specified key (`"3"` in this case).
    
4. **Checking if Key/Value Exists**:
    
    ```csharp
    Console.WriteLine($"Contains key '2': {ht.ContainsKey("2")}");
    Console.WriteLine($"Contains value 'Banana': {ht.ContainsValue("Banana")}");
    
    ```
    
    These check if the Hashtable contains a particular key or value.
    
5. **Removing an Element**:
    
    ```csharp
    ht.Remove("4");
    
    ```
    
    Removes the key-value pair with the specified key (`"4"` in this case).
    
6. **Iterating Over the Hashtable**:
    
    ```csharp
    foreach (DictionaryEntry entry in ht)
    {
        Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
    }
    
    ```
    
    Iterates over all key-value pairs in the `Hashtable`.
    
7. **Handling Missing Keys**:
    
    ```csharp
    try
    {
        Console.WriteLine($"Element with key '10': {ht["10"]}");
    }
    catch (KeyNotFoundException ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
    }
    
    ```
    
    Accessing a key that does not exist throws a `KeyNotFoundException`, which we catch and display a message.
    
8. **Updating an Element**:
    
    ```csharp
    ht["1"] = "Avocado";
    
    ```
    
    The value associated with an existing key can be updated using the indexer.
    
9. **Clearing the Hashtable**:
    
    ```csharp
    ht.Clear();
    
    ```
    
    Removes all key-value pairs from the Hashtable.
    

---

### **Expected Output**

```csharp
Hashtable contains 5 items.
Element with key '3': Cherry
Contains key '2': True
Contains value 'Banana': True

Removed key '4'. Now the Hashtable contains:
Key: 1, Value: Apple
Key: 2, Value: Banana
Key: 3, Value: Cherry
Key: 5, Value: Elderberry

Error: Key not found.
Updated element with key '1':
Key: 1, New Value: Avocado

Hashtable cleared. Now it contains 0 items.

```

---

### **Summary of Key Points**

* `Add`: Adds a key-value pair.
    
* `ContainsKey` / `ContainsValue`: Checks if a key or value exists.
    
* `Remove`: Removes a key-value pair by key.
    
* **Iteration**: Can be done using a `foreach` loop over `DictionaryEntry`.
    
* **Indexer (**`[]`): Accesses and modifies values associated with a key.
    
* `Clear`: Clears all elements in the Hashtable.
    

### BitArray class

The `BitArray` class in C# is part of the `System.Collections` namespace and represents a collection of bits (binary values, `0` or `1`). It's useful when you need to efficiently store and manipulate a large number of boolean values or binary data. Each element of a `BitArray` is a single bit, so it provides a memory-efficient way to handle binary data.

### **Key Features of** `BitArray`

1. **Memory Efficiency**: It uses a single bit per element, making it more memory-efficient than using other collections like `bool[]`.
    
2. **Indexable**: You can access and modify individual bits using an index.
    
3. **Bitwise Operations**: It supports bitwise logical operations such as AND, OR, XOR, and NOT.
    
4. **Fixed Size**: Once created, the size of a `BitArray` is fixed, but you can resize it if needed.
    
5. **Automatic Resizing**: You can increase the size of the `BitArray`, but it does not shrink automatically.
    
6. **Thread-Safety**: It is not thread-safe by default.
    

### **Basic Operations with** `BitArray`

* **Set**: Set a specific bit to `true` or `false`.
    
* **Get**: Get the value of a bit (either `true` or `false`).
    
* **AND, OR, XOR**: Perform bitwise operations.
    
* **Count**: Get the number of bits in the `BitArray`.
    

### **Example of** `BitArray` Usage

Here's a simple example demonstrating how to create and manipulate a `BitArray` in C#:

```csharp
using System;
using System.Collections;

class Program
{
    static void Main()
    {
        // Create a BitArray with 5 elements (bits) initialized to false (0)
        BitArray bits = new BitArray(5);

        // Set individual bits
        bits[0] = true; // Set bit at index 0 to true (1)
        bits[1] = true; // Set bit at index 1 to true (1)

        // Display the BitArray
        Console.WriteLine("BitArray after setting bits:");
        for (int i = 0; i < bits.Length; i++)
        {
            Console.Write(bits[i] ? "1 " : "0 ");
        }
        Console.WriteLine(); // Output: 1 1 0 0 0

        // Perform bitwise operations
        BitArray otherBits = new BitArray(new bool[] { true, false, true, false, true });

        // AND operation
        BitArray andResult = bits.And(otherBits);
        Console.WriteLine("Result of AND operation:");
        for (int i = 0; i < andResult.Length; i++)
        {
            Console.Write(andResult[i] ? "1 " : "0 ");
        }
        Console.WriteLine(); // Output: 1 0 0 0 0

        // OR operation
        BitArray orResult = bits.Or(otherBits);
        Console.WriteLine("Result of OR operation:");
        for (int i = 0; i < orResult.Length; i++)
        {
            Console.Write(orResult[i] ? "1 " : "0 ");
        }
        Console.WriteLine(); // Output: 1 1 1 0 1

        // XOR operation
        BitArray xorResult = bits.Xor(otherBits);
        Console.WriteLine("Result of XOR operation:");
        for (int i = 0; i < xorResult.Length; i++)
        {
            Console.Write(xorResult[i] ? "1 " : "0 ");
        }
        Console.WriteLine(); // Output: 0 1 1 0 1

        // NOT operation (flip the bits)
        BitArray notResult = bits.Not();
        Console.WriteLine("Result of NOT operation:");
        for (int i = 0; i < notResult.Length; i++)
        {
            Console.Write(notResult[i] ? "1 " : "0 ");
        }
        Console.WriteLine(); // Output: 0 0 1 1 1
    }
}

```

### **Explanation of Operations**

1. **Creating a** `BitArray`:
    
    * A `BitArray` is created with 5 elements. Initially, all bits are `false` (0).
        
    * `BitArray bits = new BitArray(5);`
        
2. **Setting Bits**:
    
    * You can set individual bits using the indexer (`[]`).
        
    * `bits[0] = true;` sets the bit at index `0` to `1`.
        
3. **Displaying the** `BitArray`:
    
    * A loop is used to print the bits in the array, displaying `1` or `0`.
        
4. **Bitwise Operations**:
    
    * **AND**: The `And` method performs a bitwise AND operation between two `BitArray` objects.
        
    * **OR**: The `Or` method performs a bitwise OR operation.
        
    * **XOR**: The `Xor` method performs a bitwise XOR operation.
        
    * **NOT**: The `Not` method flips all the bits in the `BitArray`.
        
5. **Output of Bitwise Operations**:
    
    * Each operation modifies the bits and the result is displayed.
        

### **Expected Output**

```csharp
BitArray after setting bits:
1 1 0 0 0
Result of AND operation:
1 0 0 0 0
Result of OR operation:
1 1 1 0 1
Result of XOR operation:
0 1 1 0 1
Result of NOT operation:
0 0 1 1 1

```

### **Key Methods of** `BitArray`

* `And(BitArray value)`: Performs a bitwise AND between the current `BitArray` and another `BitArray`.
    
* `Or(BitArray value)`: Performs a bitwise OR between the current `BitArray` and another `BitArray`.
    
* `Xor(BitArray value)`: Performs a bitwise XOR between the current `BitArray` and another `BitArray`.
    
* `Not()`: Flips all the bits in the `BitArray`.
    
* `Length`: Gets the number of bits in the `BitArray`.
    
* `Set(int index, bool value)`: Sets the bit at the specified index to a specified value (`true` or `false`).
    
* `Get(int index)`: Retrieves the value of the bit at the specified index.
    

### **When to Use** `BitArray`

1. **Memory Efficiency**: When you need to work with a large number of boolean values and memory efficiency is important.
    
2. **Bitwise Operations**: If you need to perform bitwise logical operations on collections of bits (such as in networking, compression algorithms, or cryptography).
    
3. **Flag Management**: When dealing with flags or binary options (e.g., checking permissions).
    

---

### SortedList

The `SortedList<TKey, TValue>` class in C# is part of the `System.Collections.Generic` namespace. It represents a collection of key/value pairs that are sorted by the keys and allows fast retrieval based on the key. It is similar to a `Dictionary<TKey, TValue>`, but with the added feature that the keys are automatically sorted in ascending order.

### **Key Features of** `SortedList<TKey, TValue>`

1. **Sorted by Keys**: The collection is sorted by the keys. This is useful when you need to maintain an ordered collection of items.
    
2. **Efficient Lookup**: `SortedList` provides fast lookup, similar to a `Dictionary`, but with the benefit of automatic ordering of keys.
    
3. **Index Access**: You can access both the key-value pairs and individual elements by their index, not just by key.
    
4. **Performance**: Insertions, deletions, and lookups are fast but not as fast as a `Dictionary`. The insertion might take longer due to the need to maintain the order.
    

### **Basic Operations with** `SortedList<TKey, TValue>`

* **Add**: Add a key-value pair to the list.
    
* **Get/Set**: Retrieve or modify the value associated with a specific key.
    
* **Remove**: Remove a key-value pair by key.
    
* **Index Access**: Access items using an index or a key.
    

### **Example of** `SortedList<TKey, TValue>` Usage

Here's a simple example demonstrating how to create and manipulate a `SortedList`:

```csharp
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Creating a SortedList of integers with string values
        SortedList<int, string> sortedList = new SortedList<int, string>();

        // Adding elements
        sortedList.Add(3, "Three");
        sortedList.Add(1, "One");
        sortedList.Add(2, "Two");

        // Display the SortedList (automatically sorted by key)
        Console.WriteLine("SortedList contents:");
        foreach (var kvp in sortedList)
        {
            Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
        }

        // Accessing value by key
        Console.WriteLine("\\nValue for key 2: " + sortedList[2]); // Output: Two

        // Modifying a value by key
        sortedList[2] = "Updated Two";
        Console.WriteLine("\\nUpdated value for key 2: " + sortedList[2]); // Output: Updated Two

        // Removing a key-value pair
        sortedList.Remove(1);
        Console.WriteLine("\\nAfter removing key 1:");
        foreach (var kvp in sortedList)
        {
            Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
        }

        // Checking if a key exists
        if (sortedList.ContainsKey(3))
        {
            Console.WriteLine("\\nKey 3 exists in the SortedList.");
        }

        // Accessing value by index
        Console.WriteLine("\\nElement at index 0:");
        var elementAtIndex = sortedList.ElementAt(0); // Get element at index 0
        Console.WriteLine($"Key: {elementAtIndex.Key}, Value: {elementAtIndex.Value}");
    }
}

```

### **Explanation of Operations**

1. **Creating a** `SortedList`:
    
    * A `SortedList<int, string>` is created to store integer keys and string values.
        
    * `SortedList<int, string> sortedList = new SortedList<int, string>();`
        
2. **Adding Elements**:
    
    * The `Add` method is used to insert key-value pairs.
        
    * `sortedList.Add(3, "Three");`
        
3. **Displaying the** `SortedList`:
    
    * A `foreach` loop is used to display all elements, and the list will automatically be sorted by key.
        
4. **Accessing by Key**:
    
    * The value associated with a specific key is accessed directly using `sortedList[key]`.
        
5. **Modifying Values**:
    
    * You can modify the value for a given key using the indexer (`sortedList[key] = newValue`).
        
6. **Removing Elements**:
    
    * The `Remove` method is used to delete a key-value pair by key.
        
    * `sortedList.Remove(1);`
        
7. **Checking for Key Existence**:
    
    * The `ContainsKey` method checks if a specific key exists in the list.
        
8. **Accessing by Index**:
    
    * The `ElementAt(index)` method is used to get the element at a particular index in the sorted list.
        

### **Expected Output**

```csharp
SortedList contents:
Key: 1, Value: One
Key: 2, Value: Two
Key: 3, Value: Three

Value for key 2: Two

Updated value for key 2: Updated Two

After removing key 1:
Key: 2, Value: Updated Two
Key: 3, Value: Three

Key 3 exists in the SortedList.

Element at index 0:
Key: 2, Value: Updated Two

```

### **Key Methods of** `SortedList<TKey, TValue>`

* `Add(TKey key, TValue value)`: Adds a key-value pair to the list.
    
* `Remove(TKey key)`: Removes the key-value pair with the specified key.
    
* `ContainsKey(TKey key)`: Checks if a key exists in the list.
    
* `ContainsValue(TValue value)`: Checks if a value exists in the list.
    
* `Item[TKey key]`: Indexer to get or set a value by key.
    
* `Count`: Gets the number of key-value pairs in the list.
    
* `Keys`: Gets a collection of the keys in the list.
    
* `Values`: Gets a collection of the values in the list.
    

### **When to Use** `SortedList<TKey, TValue>`

1. **Ordered Collection**: When you need to maintain a collection of items that are always sorted by key.
    
2. **Fast Lookup**: If you need to look up values by key in an ordered collection with a small overhead on insertions and deletions.
    
3. **Efficient for Smaller Collections**: While `SortedList` is efficient in terms of sorting and lookups, it may not be as efficient as `Dictionary` for larger collections because of the overhead required to maintain sorting.
    

---
