Arrays can be thought of as long rows of slots into which values can be placed. Once you have a picture of a row of slots, imagine 10 rows, one on top of another. This is the classic two-dimensional array of rows and columns. The rows run across the array and the columns run up and down the array, as shown in Figure 10-2.
A third dimension is a bit harder to imagine. Okay, now imagine four dimensions. Now imagine 10.
Those of you who are not string-theory physicists have probably given up, as have I. Multidimensional arrays are useful, however, even if you can’t quite picture what they would look like.
C# supports two types of multidimensional arrays: rectangular and jagged. In a rectangular array, every row is the same length. A jagged array, however, is an array of arrays, each of which can be a different length.
A rectangular array is an array of two (or more) dimensions. In the classic two-dimensional array, the first dimension is the number of rows and the second dimension is the number of columns.
To declare a two-dimensional array, use the following syntax:
type
[,]array-name
For example, to declare and instantiate a two-dimensional
rectangular array named myRectangularArray
that contains two rows
and three columns of integers, you would write:
int [,] myRectangularArray = new int[2,3];
Example 10-4
declares, instantiates, initializes, and prints the contents of a
two-dimensional array. In this example, a for
loop is used to initialize the elements
of the array.
Example 10-4. Rectangular arrays
using System; namespace RectangularArray { public class Tester { static void Main( ) { const int rows = 4; const int columns = 3; // declare a 4x3 integer array int[,] rectangularArray = new int[rows, columns]; // populate the array for ( int i = 0; i < rows; i++ ) { for ( int j = 0; j < columns; j++ ) { rectangularArray[i, j] = i + j; } } // report the contents of the array for ( int i = 0; i < rows; i++ ) { for ( int j = 0; j < columns; j++ ) { Console.WriteLine( "rectangularArray[{0},{1}] = {2}", i, j, rectangularArray[i, j] ); } } } } }
The output looks like this:
rectangularArray[0,0] = 0 rectangularArray[0,1] = 1 rectangularArray[0,2] = 2 rectangularArray[1,0] = 1 rectangularArray[1,1] = 2 rectangularArray[1,2] = 3 rectangularArray[2,0] = 2 rectangularArray[2,1] = 3 rectangularArray[2,2] = 4 rectangularArray[3,0] = 3 rectangularArray[3,1] = 4 rectangularArray[3,2] = 5
The brackets in the int[,]
declaration indicate that the type is an array of integers, and the
comma indicates the array has two dimensions (two commas would
indicate three dimensions, and so on). The actual instantiation of
rectangularArray
with new int[rows, columns]
sets the size of each
dimension. Here, the declaration and instantiation have been
combined.
The program fills the rectangle with a pair of nested for
loops, iterating through each column in
each row. Thus, the first element filled is rectangularArray[0,0]
, followed by rectangularArray[0,1]
and rectangularArray[0,2]
. Once this is done,
the program moves on to the next rows: rectangularArray[1,0]
, rectangularArray[1,1]
, rectangularArray[1,2]
, and so forth, until
all the columns in all the rows are filled.
Just as you can initialize a one-dimensional array using
bracketed lists of values, you can initialize a two-dimensional array
using similar syntax. Example
10-5 declares a two-dimensional array (rectangularArray
), initializes its elements
using bracketed lists of values, and then prints out the
contents.
Example 10-5. Initializing a multidimensional array
using System; namespace InitializingMultiDimensionalArray { public class Tester { static void Main( ) { const int rows = 4; const int columns = 3; // imply a 4x3 array int[,] rectangularArray = { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11} }; for ( int i = 0; i < rows; i++ ) { for ( int j = 0; j < columns; j++ ) { Console.WriteLine( "rectangularArray[{0},{1}] = {2}", i, j, rectangularArray[i, j] ); } } } } }
The output looks like this:
rectangularArrayrectangularArray[0,0] = 0 rectangularArrayrectangularArray[0,1] = 1 rectangularArrayrectangularArray[0,2] = 2 rectangularArrayrectangularArray[1,0] = 3 rectangularArrayrectangularArray[1,1] = 4 rectangularArrayrectangularArray[1,2] = 5 rectangularArrayrectangularArray[2,0] = 6 rectangularArrayrectangularArray[2,1] = 7 rectangularArrayrectangularArray[2,2] = 8 rectangularArrayrectangularArray[3,0] = 9 rectangularArrayrectangularArray[3,1] = 10 rectangularArrayrectangularArray[3,2] = 11
The preceding example is very similar to Example 10-4, but this time you imply the exact dimensions of the array by how you initialize it:
int[,] rectangularArrayrectangularArray = { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11} };
Assigning values in four bracketed lists, each consisting of three elements, implies a 4 (rows) by 3 (columns) array.
Had you written this as:
int[,] rectangularArrayrectangularArray = { {0,1,2,3}, {4,5,6,7}, {8,9,10,11} };
you would instead have implied a 3 by 4 array.
You can see that the C# compiler understands the implications of the way you grouped the input values, because it is able to access the objects with the appropriate offsets, as illustrated in the output.
C# arrays are “smart” and they keep track of their bounds. When you imply a 4 × 3 array, you must treat it as such, and not as a 3 × 4 array, or just an array of 12 integers (as you can with some other C-family languages).
A jagged array is an array of arrays. It is called “jagged” because each of the rows need not be the same size as all the others, and thus a graphical representation of the array would not be square.
When you create a jagged array, you declare the number of rows in your array. Each row will hold an array, which can be of any length. These arrays must each be declared. You can then fill in the values for the elements in these “inner” arrays.
In a jagged array, each dimension is a one-dimensional array. To declare a jagged array, use the following syntax, where the number of brackets indicates the number of dimensions of the array:
type
[] []...
For example, you would declare a two-dimensional jagged array of
integers named myJaggedArray
, as
follows:
int [] [] myJaggedArray;
Access the fifth element of the third array by writing myJaggedArray[2][4]
.
Remember that in all arrays, the first element is at offset 0 and the nth element is at offset n - 1; thus, the seventh element is at offset 6.
Example 10-6
creates a jagged array named myJaggedArray
, initializes its elements, and
then prints their content. To save space, the program takes advantage
of the fact that integer array elements are automatically initialized
to zero, and it initializes the values of only some of the
elements.
Example 10-6. Working with a jagged array
using System; namespace JaggedArray { public class Tester { static void Main( ) { const int rows = 4; // declare the jagged array as 4 rows high int[][] jaggedArray = new int[rows][]; // the first row has 5 elements jaggedArray[0] = new int[5]; // a row with 2 elements jaggedArray[1] = new int[2]; // a row with 3 elements jaggedArray[2] = new int[3]; // the last row has 5 elements jaggedArray[3] = new int[5]; // Fill some (but not all) elements of the rows jaggedArray[0][3] = 15; jaggedArray[1][1] = 12; jaggedArray[2][1] = 9; jaggedArray[2][2] = 99; jaggedArray[3][0] = 10; jaggedArray[3][1] = 11; jaggedArray[3][2] = 12; jaggedArray[3][3] = 13; jaggedArray[3][4] = 14; for ( int i = 0; i < 5; i++ ) { Console.WriteLine( "jaggedArray[0][{0}] = {1}", i, jaggedArray[0][i] ); } for ( int i = 0; i < 2; i++ ) { Console.WriteLine( "jaggedArray[1][{0}] = {1}", i, jaggedArray[1][i] ); } for ( int i = 0; i < 3; i++ ) { Console.WriteLine( "jaggedArray[2][{0}] = {1}", i, jaggedArray[2][i] ); } for ( int i = 0; i < 5; i++ ) { Console.WriteLine( "jaggedArray[3][{0}] = {1}", i, jaggedArray[3][i] ); } } } }
The output looks like this:
jaggedArray[0][0] = 0 jaggedArray[0][1] = 0 jaggedArray[0][2] = 0 jaggedArray[0][3] = 15 jaggedArray[0][4] = 0 jaggedArray[1][0] = 0 jaggedArray[1][1] = 12 jaggedArray[2][0] = 0 jaggedArray[2][1] = 9 jaggedArray[2][2] = 99 jaggedArray[3][0] = 10 jaggedArray[3][1] = 11 jaggedArray[3][2] = 12 jaggedArray[3][3] = 13 jaggedArray[3][4] = 14
In this example, a jagged array is created with four rows:
int[][] jaggedArray = new int[rows][];
Notice that the second dimension is not specified. This is set by creating a new array for each row. Each of these arrays can have a different size:
// the first row has 5 elements jaggedArray[0] = new int[5]; // a row with 2 elements jaggedArray[1] = new int[2]; // a row with 3 elements jaggedArray[2] = new int[3]; // the last row has 5 elements jaggedArray[3] = new int[5];
Once an array is specified for each row, you need only populate the various members of each array and then print out their contents to ensure that all went as expected.
Another way of outputting the values would be to use two nested
for
loops, and use the Length
property of the array to control the
loop:
for (int i = 0; i < jaggedArray.Length; i++ ) { for (int j = 0; j < jaggedArray[i].Length; j++) { Console.WriteLine("jaggedArray[{0}][{1}] = {2}", i, j, jaggedArray[i][j]); } }
In this case, the “outer” for
loop iterates over the rows in the array. The “inner” loop outputs
each column in the given row. Because you’re using Length
to control how many times the loop
runs, it doesn’t matter that each row is a different length.
Notice that when you access the members of the rectangular array, you put the indexes all within one set of square brackets:
rectangularArrayrectangularArray[i,j]
while with a jagged array, you need a pair of brackets:
jaggedArray[i][j]
You can keep this straight by thinking of the first as a single array of more than one dimension and the jagged array as an array of arrays .