1. How jagged arrays are different from two-dimensional arrays
Here we are at a topic a lot of folks call "arrays of arrays" or "jagged arrays" — in English, jagged arrays. Unlike two-dimensional arrays, jagged arrays let you store columns of different lengths. It's like having a complex of buildings, where each building has its own number of apartments — one building has 5 apartments, another has 20, and a third has just one.
A jagged array is an array where each element is itself an array. The inner arrays (also called "subarrays") can have different lengths.
The main difference:
- In a two-dimensional array, every "row" (and every "column") has the same number of elements. Example: int[,] grid = new int[3, 5]; — you always have 3 rows of 5 elements each.
- In a jagged array, each row can be a different length! Example: int[][] jagged = new int[3][]; — and then you initialize each row (subarray) however you want.
Here's what it looks like visually:
| Two-dimensional array | Jagged array | |
|---|---|---|
| Number of elements | Strictly fixed (like 3x5) | Can be different for each row |
| Indexing | |
|
| Flexibility | Low | High |
| Use cases | Tables, math stuff | Uneven data: lists of students with different numbers of grades, triangles |
Visualization: comparing two-dimensional and jagged arrays
Two-dimensional array (3x3):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘
Jagged array (different lengths):
┌───┬───┐
│ 1 │ 2 │
├───┼───┼───┬───┐
│ 3 │ 4 │ 5 │ 6 │
├───┼───┴───┴───┘
│ 7 │
└───┘
2. Syntax for declaring and initializing a jagged array
Declaring a jagged array isn't any scarier than declaring other types! Don't freak out about the double square brackets:
int[][] jaggedArray = new int[3][];
This means we've got an array of 3 elements, and each of those is also an array of ints. But the inner arrays aren't created yet! Let's break this down for better understanding.
Step-by-step initialization of a jagged array
Step 1 — create the main (outer) array:
int[][] jaggedArray = new int[3][];
Now we've got 3 "rows", but they're all still null.
Step 2 — create and fill the inner arrays (subarrays):
For example, let's make the first row length 2, the second — 4, the third — 3:
jaggedArray[0] = new int[2]; // 2 elements in the first row
jaggedArray[1] = new int[4]; // 4 elements in the second row
jaggedArray[2] = new int[3]; // 3 elements in the third row
Step 3 — fill with values:
The inner arrays are just regular arrays! For example:
jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;
jaggedArray[1][0] = 3;
jaggedArray[1][1] = 4;
jaggedArray[1][2] = 5;
jaggedArray[1][3] = 6;
jaggedArray[2][0] = 7;
jaggedArray[2][1] = 8;
jaggedArray[2][2] = 9;
Quick initialization of a jagged array
You can create and fill a jagged array right away if you know the values in advance:
int[][] jaggedArray = new int[][]
{
new int[] { 1, 2 },
new int[] { 3, 4, 5, 6 },
new int[] { 7, 8, 9 }
};
Or a bit shorter, skipping the type for the inner arrays:
int[][] jaggedArray = {
new[] { 1, 2 },
new[] { 3, 4, 5, 6 },
new[] { 7, 8, 9 }
};
3. Looping through and working with jagged arrays
Looping through a jagged array isn't any harder than a two-dimensional one, but now the outer loop goes through the rows, and the inner loop goes through the elements of each row (which can be different lengths):
for (int i = 0; i < jaggedArray.Length; i++)
{
Console.WriteLine($"Row {i}:");
for (int j = 0; j < jaggedArray[i].Length; j++)
{
Console.Write($"{jaggedArray[i][j]} ");
}
Console.WriteLine();
}
Result on the screen:
Row 0:
1 2
Row 1:
3 4 5 6
Row 2:
7 8 9
You can use foreach so you don't have to think about indexes:
foreach (int[] row in jaggedArray)
{
foreach (int value in row)
{
Console.Write($"{value} ");
}
Console.WriteLine();
}
4. How arrays of arrays work under the hood
Now you'll find out how arrays of arrays actually work. Ready?
With a regular array, "the array variable stores a reference to a container that holds the array elements." But with jagged arrays, things get a bit more wild: the array-of-arrays variable stores a reference to a container that holds references to one-dimensional arrays. It's easier to see than to explain a hundred times:
On the left we've got the "array-of-arrays variable" that stores a reference to the "array container object." In the middle is the "array container object," whose cells store references to one-dimensional arrays — the rows of the jagged array. And on the right you see four one-dimensional arrays — the rows of our jagged array.
That's how jagged arrays really work. And this approach gives a C# programmer a few perks:
First off, since the "container of containers" stores references to "row arrays," we can swap rows super quickly and easily. To access the "container of containers," you just need to use one index instead of two. Example:int[][] data = new int[2][];
data[0] = new int[5]; // first row — array of 5 elements
data[1] = new int[5]; // second row — array of 5 elements
int[] row1 = data[0];
int[] row2 = data[1];
Here's how you can swap rows with this code:
// Important matrix with data
int[][] matrix = {
new int[] {1, 2, 3, 4, 5},
new int[] {5, 4, 3, 2, 1}
};
int[] tmp = matrix[0];
matrix[0] = matrix[1];
matrix[1] = tmp;
If you access a cell of a two-dimensional array but only specify one index after the array name, you're actually accessing the container of containers, whose cells store references to regular one-dimensional arrays.
5. Typical use cases for jagged arrays
When is a jagged array more useful than a two-dimensional one?
- If you're storing a different number of something for each user: grades for subjects, purchases, comments, and so on.
- If your data has a triangular or stepped structure (like for printing pyramids, Pascal's triangles, etc.).
- If you want to save memory: in a two-dimensional array, all rows are fixed, but in a jagged array — only as many elements as you need.
Real-life example: student grades manager
Let's expand our learning project! Let's say each student can have a different number of grades for each subject. For example, some turn in more assignments, some less. A jagged array is perfect for this.
Let's say we've got three students, and here are their grades for different math assignments:
| Student | Grades |
|---|---|
| 0 | 5, 4 |
| 1 | 3, 4, 4 |
| 2 | 5 |
Let's declare this array:
int[][] studentMarks = new int[3][];
studentMarks[0] = new int[] { 5, 4 }; // First student - 2 grades
studentMarks[1] = new int[] { 3, 4, 4 }; // Second student - 3 grades
studentMarks[2] = new int[] { 5 }; // Third student - 1 grade
Let's print each student's grades:
for (int i = 0; i < studentMarks.Length; i++)
{
Console.Write($"Student {i}: ");
for (int j = 0; j < studentMarks[i].Length; j++)
{
Console.Write(studentMarks[i][j] + " ");
}
Console.WriteLine();
}
Using jagged arrays with other types
A jagged array can be an array of anything: strings, arrays of other arrays (even deeper!), even your own objects.
Example: array of strings
string[][] groups = new string[][]
{
new string[] { "Ivan", "Petr" },
new string[] { "Maria", "Aleksei", "Sergei" },
new string[] { "Vasilisa" }
};
6. Features and possible mistakes
Jagged arrays are super flexible, but there are traps everywhere.
- If you didn't initialize one of the inner arrays (jaggedArray[1] = ...), trying to access it will throw a NullReferenceException. Don't forget to initialize every inner array!
- Not all rows (subarrays) are the same length. If you use a fixed index for the second dimension, you might go out of bounds.
- Don't mix them up with two-dimensional arrays! Indexing looks like this: array[i][j], not array[i, j].
GO TO FULL VERSION