Jagged Arrays

Vaibhav • September 12, 2025

Arrays are fundamental, but real-world data isn’t always a neat rectangle. When each “row” needs a different length - for example, students with varying numbers of test scores or levels with different enemy counts - a jagged array (an array of arrays) gives you flexible, memory-friendly storage. In this article we’ll explain what jagged arrays are, how to create and use them, why their memory layout matters, and when to prefer them over rectangular (multidimensional) arrays. All examples are short, well-indented, and explained line-by-line.

What is a jagged array?

A jagged array is simply an array whose elements are themselves arrays. In C# this is written with double-square-brackets, for example int[][]. Each row can have a different length, making jagged arrays ideal for non-uniform data.

int[][] jagged = new int[3][]; // parent array with 3 rows; each row is currently null

Key points:

  1. The parent array holds references to child arrays (not the child data inline).
  2. Each child (row) must be initialized separately.
  3. Rows can differ in length - that’s the main benefit.

Initializing jagged arrays

You can initialize rows one by one or inline. Below are both styles - both are common and readable.

// Step-by-step initialization int[][] jagged = new int[3][]; // create parent array with 3 slots jagged[0] = new int[2]; // row 0 has 2 elements jagged[1] = new int[4]; // row 1 has 4 elements jagged[2] = new int[3]; // row 2 has 3 elements

// Inline initializer (equivalent)
int[][] predefined = new int[][]
{
new int[] { 1, 2 },
new int[] { 3, 4, 5, 6 },
new int[] { 7, 8, 9 }
};

Line-by-line: the parent array (jagged) has three slots. Each slot is assigned a fresh array with its own Length. With the inline initializer, you both allocate the child arrays and populate them with values in one expression.

Accessing and iterating

Access uses two indexes: jagged[row][col]. When iterating, always query the current row’s length rather than assuming a uniform size.

// Safe iteration of a jagged array for (int r = 0; r < predefined.Length; r++) { for (int c = 0; c < predefined[r].Length; c++) { Console.Write(predefined[r][c] + " "); } Console.WriteLine(); }

This prints:

1 2 3 4 5 6 7 8 9

When to use jagged arrays

Good scenarios include:

  • Irregular row lengths - student test scores where each student took a different number of exams.
  • Memory savings - avoid allocating unused cells in a rectangular array when rows vary widely in size.
  • Representing lists-of-lists where each sub-list has independent lifetime or semantics.

Prefer jagged arrays when rows genuinely differ in length. If every row is the same length, a rectangular multidimensional array (int[,]) is usually clearer and more cache-friendly.

Memory layout and performance implications

Jagged arrays are implemented as a parent array of references to independently allocated child arrays. That means:

  • Each child array lives at its own location on the managed heap.
  • Accessing jagged[r][c] does an extra pointer dereference compared to a rectangular array.
  • Because rows are separate, rows can be collected or replaced independently.

The extra indirection makes jagged arrays slightly less cache-friendly than contiguous rectangular arrays. For very hot inner loops over truly rectangular data, int[,] can be faster.

Practical example - student grades

A small real-world-style example (no classes or complex types - just arrays and loops) shows why jagged arrays are convenient.

// Store student grades where each student has taken a different number of tests int[][] grades = new int[][] { new int[] { 92, 85 }, // student 0 new int[] { 78, 88, 91, 95 }, // student 1 new int[] { 100 } // student 2 };

// Compute and print simple averages
for (int s = 0; s < grades.Length; s++)
{
int sum = 0;
for (int t = 0; t < grades[s].Length; t++)
{
sum += grades[s][t];
}

double avg = (double)sum / grades[s].Length;
Console.WriteLine($"Student {s} average: {avg:F1}");


}

Jagged arrays vs multidimensional arrays

Quick comparison to help you choose:

  • Jagged - flexible lengths, parent holds references, slight extra indirection, easier when rows vary.
  • Rectangular (multidimensional) - single contiguous block, better locality, simpler when shape is fixed.

The runtime stores jagged-array child arrays independently, so you can replace an entire row easily by assigning a new array to jagged[row]. That’s handy for dynamic datasets.

Common pitfalls

  • Forgetting to initialize a row: accessing jagged[0][0] before setting jagged[0] causes a NullReferenceException.
  • Assuming uniform length: always check jagged[row].Length.
  • Resizing rows: arrays are fixed-size once created - to change a row’s length you must allocate a new array and copy data if needed.

Summary

Jagged arrays (arrays of arrays) are the right tool when rows differ in size or when you want independent row lifetime. They’re simple to initialize, access, and iterate - just remember to initialize each row and use the row length when iterating. When rows are uniform and performance-critical, consider rectangular arrays; for variable row lengths, jagged arrays keep memory usage lean and intent clear.

Next up: in the following article we’ll explore array methods and properties - handy built-in helpers like Length, Array.Copy, and Array.Sort that are useful with both jagged and rectangular arrays.