Skip to content

C# Arrays and Collections

This chapter will detail array and collection types in C#, including one-dimensional arrays, multi-dimensional arrays, jagged arrays, and common collection classes, helping you effectively store and manipulate multiple data items.

Array Basics

One-Dimensional Arrays

Array Declaration and Initialization

csharp
// Declare array
int[] numbers;                    // Declare but not initialized
string[] names = new string[5];   // Declare and allocate space

// Initialize array
int[] scores = new int[3];        // Create array with 3 elements, default value 0
scores[0] = 85;
scores[1] = 92;
scores[2] = 78;

// Declare and initialize
int[] grades = new int[] { 85, 92, 78, 96, 88 };
string[] cities = { "Beijing", "Shanghai", "Guangzhou", "Shenzhen" };  // Simplified syntax

// Use var keyword
var temperatures = new double[] { 25.5, 28.3, 22.1, 30.0 };

Array Access and Operations

csharp
int[] numbers = { 10, 20, 30, 40, 50 };

// Access array elements
Console.WriteLine($"First element: {numbers[0]}");      // 10
Console.WriteLine($"Last element: {numbers[4]}");     // 50

// Modify array elements
numbers[2] = 35;
Console.WriteLine($"Modified third element: {numbers[2]}"); // 35

// Array length
Console.WriteLine($"Array length: {numbers.Length}");

// Iterate through array
Console.WriteLine("Using for loop:");
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine($"numbers[{i}] = {numbers[i]}");
}

// Using foreach loop
Console.WriteLine("Using foreach loop:");
foreach (int num in numbers)
{
    Console.WriteLine($"Number: {num}");
}

Multi-Dimensional Arrays

Two-Dimensional Arrays

csharp
// Declare 2D array
int[,] matrix = new int[3, 4];

// Initialize 2D array
int[,] grid = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// Access 2D array elements
Console.WriteLine($"Element [0,0]: {grid[0, 0]}");  // 1
Console.WriteLine($"Element [1,2]: {grid[1, 2]}");  // 7

// Modify 2D array elements
grid[2, 3] = 99;

// Get dimensions
Console.WriteLine($"Rows: {grid.GetLength(0)}");  // 3
Console.WriteLine($"Columns: {grid.GetLength(1)}"); // 4

// Iterate through 2D array
for (int row = 0; row < grid.GetLength(0); row++)
{
    for (int col = 0; col < grid.GetLength(1); col++)
    {
        Console.Write($"{grid[row, col]} ");
    }
    Console.WriteLine();
}

Three-Dimensional Arrays

csharp
// Declare 3D array
int[,,] cube = new int[2, 3, 4];

// Initialize 3D array
int[,,] threeD = {
    {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    },
    {
        {13, 14, 15, 16},
        {17, 18, 19, 20},
        {21, 22, 23, 24}
    }
};

// Access 3D array
Console.WriteLine($"Element [0,1,2]: {threeD[0, 1, 2]}");  // 7

Jagged Arrays

csharp
// Declare jagged array
int[][] jagged = new int[3][];

// Initialize jagged array
jagged[0] = new int[] { 1, 2, 3 };
jagged[1] = new int[] { 4, 5 };
jagged[2] = new int[] { 6, 7, 8, 9 };

// Alternative initialization
int[][] jagged2 = {
    new int[] { 1, 2, 3 },
    new int[] { 4, 5 },
    new int[] { 6, 7, 8, 9 }
};

// Access jagged array
Console.WriteLine($"Element [0][1]: {jagged[0][1]}");  // 2

// Iterate through jagged array
for (int i = 0; i < jagged.Length; i++)
{
    Console.WriteLine($"Row {i}:");
    for (int j = 0; j < jagged[i].Length; j++)
    {
        Console.Write($"{jagged[i][j]} ");
    }
    Console.WriteLine();
}

Array Operations

Array Methods

csharp
int[] numbers = { 5, 2, 8, 1, 9, 3 };

// Sort array
Array.Sort(numbers);
Console.WriteLine("Sorted array: " + string.Join(", ", numbers));

// Reverse array
Array.Reverse(numbers);
Console.WriteLine("Reversed array: " + string.Join(", ", numbers));

// Clear array
Array.Clear(numbers, 0, numbers.Length);
Console.WriteLine($"After clear, first element: {numbers[0]}");  // 0

// Copy array
int[] destination = new int[6];
Array.Copy(numbers, destination, numbers.Length);

// Find element
int index = Array.IndexOf(numbers, 8);
Console.WriteLine($"Index of 8: {index}");

// Check if element exists
bool contains = Array.Exists(numbers, x => x == 8);
Console.WriteLine($"Contains 8: {contains}");

// Find all matching elements
int[] evenNumbers = Array.FindAll(numbers, x => x % 2 == 0);
Console.WriteLine("Even numbers: " + string.Join(", ", evenNumbers));

LINQ with Arrays

csharp
int[] numbers = { 5, 2, 8, 1, 9, 3, 7, 4, 6 };

// LINQ queries
var sorted = numbers.OrderBy(x => x);
var reversed = numbers.Reverse();
var distinct = numbers.Distinct();
var first = numbers.First();
var last = numbers.Last();
var where = numbers.Where(x => x > 5);
var select = numbers.Select(x => x * 2);

Console.WriteLine("Sorted: " + string.Join(", ", sorted));
Console.WriteLine("Reversed: " + string.Join(", ", reversed));
Console.WriteLine("Distinct: " + string.Join(", ", distinct));
Console.WriteLine("Numbers > 5: " + string.Join(", ", where));
Console.WriteLine("Doubled: " + string.Join(", ", select));

Collections

List<T>

Basic List Operations

csharp
// Create list
List<int> numbers = new List<int>();
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };

// Add elements
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);

// Add range
numbers.AddRange(new int[] { 40, 50, 60 });

// Insert at specific position
numbers.Insert(2, 25);  // Insert 25 at index 2

// Access elements
Console.WriteLine($"First element: {numbers[0]}");
Console.WriteLine($"Count: {numbers.Count}");

// Remove elements
numbers.Remove(20);        // Remove first occurrence
numbers.RemoveAt(0);       // Remove at index
numbers.RemoveAll(x => x > 40);  // Remove all matching elements

// Check if contains
bool hasTen = numbers.Contains(10);

// Clear list
numbers.Clear();

List Methods

csharp
List<int> numbers = new List<int> { 5, 2, 8, 1, 9, 3 };

// Sort
numbers.Sort();

// Binary search
int index = numbers.BinarySearch(8);

// Find
int firstEven = numbers.Find(x => x % 2 == 0);
List<int> allEven = numbers.FindAll(x => x % 2 == 0);

// Convert to array
int[] array = numbers.ToArray();

// Capacity management
Console.WriteLine($"Count: {numbers.Count}");
Console.WriteLine($"Capacity: {numbers.Capacity}");
numbers.TrimExcess();  // Reduce capacity to match count

Dictionary<TKey, TValue>

Basic Dictionary Operations

csharp
// Create dictionary
Dictionary<string, int> ages = new Dictionary<string, int>();

// Add elements
ages["Alice"] = 25;
ages["Bob"] = 30;
ages.Add("Charlie", 35);

// Alternative add with check
if (!ages.ContainsKey("David"))
{
    ages["David"] = 28;
}

// Access elements
Console.WriteLine($"Alice's age: {ages["Alice"]}");

// Safe access
if (ages.TryGetValue("Eve", out int eveAge))
{
    Console.WriteLine($"Eve's age: {eveAge}");
}
else
{
    Console.WriteLine("Eve not found");
}

// Remove elements
ages.Remove("Bob");

// Check if contains
bool hasAlice = ages.ContainsKey("Alice");

// Get all keys and values
Console.WriteLine("Keys: " + string.Join(", ", ages.Keys));
Console.WriteLine("Values: " + string.Join(", ", ages.Values));

// Iterate through dictionary
foreach (KeyValuePair<string, int> kvp in ages)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

HashSet<T>

Basic HashSet Operations

csharp
// Create hash set
HashSet<string> uniqueNames = new HashSet<string>();

// Add elements
uniqueNames.Add("Alice");
uniqueNames.Add("Bob");
uniqueNames.Add("Alice");  // Duplicate, won't be added

// Check if added
bool added = uniqueNames.Add("Charlie");

// Check if contains
bool hasAlice = uniqueNames.Contains("Alice");

// Remove elements
uniqueNames.Remove("Bob");

// Set operations
HashSet<string> set1 = new HashSet<string> { "A", "B", "C" };
HashSet<string> set2 = new HashSet<string> { "B", "C", "D" };

// Union
set1.UnionWith(set2);  // { "A", "B", "C", "D" }

// Intersection
set1.IntersectWith(set2);  // { "B", "C" }

// Difference
set1.ExceptWith(set2);  // { "A" }

// Symmetric difference
set1.SymmetricExceptWith(set2);  // { "A", "D" }

Queue<T>

Basic Queue Operations

csharp
// Create queue
Queue<string> messageQueue = new Queue<string>();

// Enqueue (add to end)
messageQueue.Enqueue("First");
messageQueue.Enqueue("Second");
messageQueue.Enqueue("Third");

// Peek (look at first without removing)
string firstMessage = messageQueue.Peek();

// Dequeue (remove from front)
string dequeued = messageQueue.Dequeue();

// Check count
Console.WriteLine($"Messages in queue: {messageQueue.Count}");

// Clear queue
messageQueue.Clear();

// Convert to array
string[] messages = messageQueue.ToArray();

Stack<T>

Basic Stack Operations

csharp
// Create stack
Stack<int> numberStack = new Stack<int>();

// Push (add to top)
numberStack.Push(10);
numberStack.Push(20);
numberStack.Push(30);

// Peek (look at top without removing)
int topNumber = numberStack.Peek();

// Pop (remove from top)
int popped = numberStack.Pop();

// Check count
Console.WriteLine($"Numbers in stack: {numberStack.Count}");

// Clear stack
numberStack.Clear();

// Convert to array
int[] numbers = numberStack.ToArray();

Collection Interfaces

IEnumerable<T>

csharp
// Custom collection implementing IEnumerable<T>
public class MyCollection : IEnumerable<int>
{
    private List<int> items = new List<int>();
    
    public void Add(int item) => items.Add(item);
    
    public IEnumerator<int> GetEnumerator()
    {
        return items.GetEnumerator();
    }
    
    IEnumerator IEnumerable.GetEnumerator()
    {
        return items.GetEnumerator();
    }
}

// Usage
MyCollection collection = new MyCollection();
collection.Add(1);
collection.Add(2);
collection.Add(3);

foreach (int item in collection)
{
    Console.WriteLine(item);
}

ICollection<T>

csharp
// Collection with add/remove operations
public class MyCollection : ICollection<int>
{
    private List<int> items = new List<int>();
    
    public int Count => items.Count;
    public bool IsReadOnly => false;
    
    public void Add(int item) => items.Add(item);
    public bool Remove(int item) => items.Remove(item);
    public void Clear() => items.Clear();
    public bool Contains(int item) => items.Contains(item);
    
    public void CopyTo(int[] array, int arrayIndex)
    {
        items.CopyTo(array, arrayIndex);
    }
    
    public IEnumerator<int> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
}

Advanced Collection Features

Collection Initializers

csharp
// Dictionary initializer
Dictionary<string, int> studentGrades = new Dictionary<string, int>
{
    ["Alice"] = 95,
    ["Bob"] = 87,
    ["Charlie"] = 92
};

// List initializer
List<Person> people = new List<Person>
{
    new Person { Name = "Alice", Age = 25 },
    new Person { Name = "Bob", Age = 30 },
    new Person { Name = "Charlie", Age = 35 }
};

Collection Expressions (C# 12.0+)

csharp
// Spread operator
List<int> first = new List<int> { 1, 2, 3 };
List<int> second = new List<int> { 4, 5, 6 };

List<int> combined = [.. first, .. second];  // { 1, 2, 3, 4, 5, 6 }

// Array expressions
int[] array1 = { 1, 2, 3 };
int[] array2 = { 4, 5, 6 };
int[] combinedArray = [.. array1, .. array2];

Performance Considerations

Array vs List Performance

csharp
// Array: Fixed size, faster access
int[] array = new int[1000];
for (int i = 0; i < 1000; i++)
{
    array[i] = i;  // Fast assignment
}

// List: Dynamic size, slightly slower access
List<int> list = new List<int>(1000);
for (int i = 0; i < 1000; i++)
{
    list.Add(i);  // Slower than array assignment
}

Choosing the Right Collection

csharp
// Use Array when:
// - Fixed number of elements
// - Performance is critical
// - Elements are value types

// Use List&lt;T&gt; when:
// - Need to add/remove elements frequently
// - Need indexed access
// - Collection size changes

// Use Dictionary&lt;TKey,TValue&gt; when:
// - Need key-value lookup
// - Fast access by key is important
// - Unique keys

// Use HashSet&lt;T&gt; when:
// - Need fast membership testing
// - No duplicate elements
// - Set operations are needed

// Use Queue&lt;T&gt; when:
// - FIFO (First In, First Out) behavior
// - Processing items in order

// Use Stack&lt;T&gt; when:
// - LIFO (Last In, First Out) behavior
// - Need to process items in reverse order

Practical Examples

Data Processing Pipeline

csharp
public class DataProcessor
{
    public static List<int> ProcessNumbers(IEnumerable<int> input)
    {
        return input
            .Where(x => x > 0)                    // Filter positive numbers
            .Select(x => x * x)                       // Square each number
            .OrderBy(x => x)                           // Sort ascending
            .Take(10)                                   // Take first 10
            .ToList();                                   // Convert to list
    }
    
    public static Dictionary<string, List<int>> GroupByRange(List<int> numbers)
    {
        return numbers
            .GroupBy(x => 
                x >= 0 && x <= 9 ? "0-9" :
                x >= 10 && x <= 19 ? "10-19" :
                x >= 20 && x <= 29 ? "20-29" :
                x >= 30 && x <= 39 ? "30-39" :
                "40+")
            .ToDictionary(g => g.Key, g => g.ToList());
    }
}

Inventory Management

csharp
public class InventoryManager
{
    private Dictionary<string, int> inventory = new Dictionary<string, int>();
    
    public void AddItem(string item, int quantity)
    {
        if (inventory.ContainsKey(item))
            inventory[item] += quantity;
        else
            inventory[item] = quantity;
    }
    
    public bool RemoveItem(string item, int quantity)
    {
        if (!inventory.ContainsKey(item) || inventory[item] < quantity)
            return false;
        
        inventory[item] -= quantity;
        if (inventory[item] == 0)
            inventory.Remove(item);
        
        return true;
    }
    
    public int GetQuantity(string item)
    {
        return inventory.TryGetValue(item, out int quantity) ? quantity : 0;
    }
    
    public List<string> GetLowStockItems(int threshold)
    {
        return inventory
            .Where(kvp => kvp.Value < threshold)
            .Select(kvp => kvp.Key)
            .ToList();
    }
    
    public void PrintInventory()
    {
        Console.WriteLine("Current Inventory:");
        foreach (var kvp in inventory)
        {
            Console.WriteLine($"{kvp.Key}: {kvp.Value}");
        }
    }
}

Best Practices

Collection Selection Guidelines

csharp
// Choose appropriate collection based on requirements
public class CollectionSelector
{
    public static ICollection<T> ChooseCollection<T>(
        bool needsFixedSize,
        bool needsFastLookup,
        bool needsOrdering,
        bool needsDuplicates)
    {
        if (needsFixedSize)
            return new T[10];  // Array
        
        if (needsFastLookup && !needsDuplicates)
            return new HashSet<T>();
        
        if (needsFastLookup && needsDuplicates)
            return new Dictionary<T, T>().Values.ToList();
        
        if (needsOrdering)
            return new List&lt;T&gt;();
        
        if (needsOrdering)
            return new List&lt;T&gt;();
        
        return new List&lt;T&gt;();  // Default choice
    }
}

Memory Management

csharp
// Use appropriate initial capacity
List<int> numbers = new List<int>(1000);  // Pre-allocate capacity

// Clear collections properly
public void ProcessLargeData()
{
    var data = new List<int>(100000);
    
    try
    {
        // Process data
    }
    finally
    {
        data.Clear();  // Release memory
        data.TrimExcess();  // Reduce capacity
    }
}

Summary

In this chapter, you learned:

  • Array types: 1D, 2D, 3D, jagged arrays
  • Array operations and LINQ integration
  • Collection types: List<T>, Dictionary<TKey,TValue>, HashSet<T>, Queue<T>, Stack<T>
  • Collection interfaces and custom collections
  • Performance considerations
  • Practical examples and best practices

Arrays and collections are essential for managing multiple data items in C#. They provide the foundation for data structures and algorithms. In the next chapter, we'll explore string processing for text manipulation and formatting.

Content is for learning and research only.