C# Jagged Array: Syntax, Usage, and Performance
c# jagged array: Learn how to declare, initialize, and iterate C# jagged arrays, understand their memory layout, and see when they outperform multidimensional arrays.
A C# jagged array is an array whose elements are arrays themselves. This means it can hold rows of different lengths, which makes it distinct from a rectangular multidimensional array. If you are working with data where each row has a different number of columns, a jagged array is often the natural fit.
Declaring a Jagged Array
In C#, you declare a jagged array by specifying multiple bracket pairs. The first pair indicates the number of rows, and the second pair is left empty because each row will hold its own array.
int[][] jagged = new int[3][];
This declaration creates an array that can hold three references to int[] arrays. At this point, the sub-arrays are null. You must initialize each row before you can assign values to its elements.
You can also declare a jagged array without immediately specifying the number of rows:
int[][] jagged;
But you must assign it before use, just like any other array.
Initializing Jagged Arrays
There are several ways to initialize a jagged array. The most explicit approach is to create each row individually:
int[][] jagged = new int[3][]; jagged[0] = new int[2] { 1, 2 }; jagged[1] = new int[4] { 3, 4, 5, 6 }; jagged[2] = new int[1] { 7 };
You can also use collection initializers directly:
int[][] jagged = new int[][] { new int[] { 1, 2 }, new int[] { 3, 4, 5, 6 }, new int[] { 7 } };
Or, with C# 12 and later, you can use the simplified collection expression syntax:
int[][] jagged = [[1, 2], [3, 4, 5, 6], [7]];
This syntax is more concise, but it still creates a separate array object for each row.
Accessing and Iterating Elements
Accessing an element in a jagged array requires two indexes: the row and the column within that row. For example, jagged[1][2] returns the value 5 from the array defined above.
To iterate over all elements, you typically use nested loops:
for (int row = 0; row < jagged.Length; row++) { for (int col = 0; col < jagged[row].Length; col++) { Console.Write($"{jagged[row][col]} "); } Console.WriteLine(); }
A foreach loop works as well, but you need to handle each sub-array explicitly:
foreach (int[] row in jagged) { foreach (int value in row) { Console.Write($"{value} "); } Console.WriteLine(); }
Notice that jagged[row].Length is used in the inner loop because each row can have a different length. This is the key difference from a rectangular array, where the length of every row is fixed.
Jagged Arrays vs. Multidimensional Arrays
C# also provides rectangular multidimensional arrays, declared as int[,]. The choice between the two affects both syntax and runtime behavior.
| Aspect | Jagged Array (int[][]) | Multidimensional Array (int[,]) |
|---|---|---|
| Declaration | new int[rows][] | new int[rows, cols] |
| Row length | Can vary per row | Fixed for all rows |
| Memory layout | Array of references to separate arrays | Single contiguous block |
| Element access | array[row][col] | array[row, col] |
| CLR type | System.Int32[][] | System.Int32[,] |
| Iteration speed | May be faster for row-wise access | Slightly slower due to bounds checks on two dimensions |
For data that is naturally rectangular, a multidimensional array is simpler to declare and access. But for data with variable-length rows, a jagged array avoids wasted memory and is often more intuitive.
Memory Layout and Performance Considerations
A jagged array is a collection of references. Each sub-array is a separate object on the managed heap, and the outer array holds references to those objects. This means that accessing jagged[row][col] involves two pointer dereferences: first to get the sub-array reference, then to get the element within that sub-array.
In contrast, a multidimensional array is a single contiguous block of memory. This can improve cache locality when you iterate over the entire array in row-major order, because the elements are stored sequentially. However, the CLR performs additional bounds checking for each dimension, which can add overhead.
For jagged arrays, each row is a separate object, so the rows may not be contiguous in memory. This can hurt cache performance if you frequently jump between rows. That said, when you iterate row by row, the inner loop only touches one contiguous block at a time, which can be efficient.
There is no universal performance winner. The right choice depends on your access pattern and whether row lengths vary. If you need to store triangular data, a jagged array avoids the memory waste of a rectangular array. If your data is always rectangular and you need to traverse it in a tight loop, a multidimensional array might be slightly faster due to better locality.
Common Pitfalls and Edge Cases
One of the most frequent mistakes is forgetting to initialize the sub-arrays. If you declare int[][] jagged = new int[3][]; and then try to assign jagged[0][0] = 5;, you will get a NullReferenceException because jagged[0] is null.
Another issue is mixing up the index order. With a jagged array, you must always access jagged[row][col]. Using a single index like jagged[row] gives you the entire sub-array, not an element.
When you pass a jagged array to a method, the method receives a reference to the outer array. Modifications to the outer array's references (e.g., replacing a row) are visible to the caller, but modifications to the inner arrays' elements are also visible because they are the same objects. This is standard reference-type behavior.
Finally, be aware that jagged.Length gives the number of rows, not the total number of elements. To get the total count, you need to sum the lengths of all rows:
int total = 0; foreach (int[] row in jagged) total += row.Length;
Choosing Between Jagged and Rectangular Arrays
Use a jagged array when the data is inherently non-rectangular, such as a triangle, a sparse matrix, or a set of rows with varying lengths. It also makes sense when you need to replace entire rows frequently, because you can assign a new array to a row without reallocating the whole structure.
Use a rectangular multidimensional array when the data is always the same width and height, and you want the simplest syntax and a single contiguous memory block. For example, a bitmap or a matrix with fixed dimensions is better represented as int[,].
If you are unsure, start with a jagged array if you need flexibility. If profiling shows that cache misses are hurting performance, consider switching to a rectangular array only if your data can be padded to fit a fixed shape. Otherwise, keep the jagged structure and optimize your iteration order.