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]}"); // 7Jagged 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 countDictionary<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<T> when:
// - Need to add/remove elements frequently
// - Need indexed access
// - Collection size changes
// Use Dictionary<TKey,TValue> when:
// - Need key-value lookup
// - Fast access by key is important
// - Unique keys
// Use HashSet<T> when:
// - Need fast membership testing
// - No duplicate elements
// - Set operations are needed
// Use Queue<T> when:
// - FIFO (First In, First Out) behavior
// - Processing items in order
// Use Stack<T> when:
// - LIFO (Last In, First Out) behavior
// - Need to process items in reverse orderPractical 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<T>();
if (needsOrdering)
return new List<T>();
return new List<T>(); // 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.