A two-dimensional array is used to store elements that are indexed by two coordinates. In C# we separate the indexes with a comma. We can create 2D arrays of any element type.
We can initialize 2D arrays with a single statement—all the memory is part of a single region. Also remember that jagged arrays can represent a 2D space.
Here we show a 2-dimensional string
array. The program creates a 2x2 string
array and then prints out all 4 elements with Console.WriteLine
.
string
.using System; // Part 1: create 2D array of strings. string[,] array = new string[,] { { "cat", "dog" }, { "bird", "fish" }, }; // Part 2: access (and print) values. Console.WriteLine(array[0, 0]); Console.WriteLine(array[0, 1]); Console.WriteLine(array[1, 0]); Console.WriteLine(array[1, 1]);cat dog bird fish
Every array has a rank: this is the number of dimensions in the array. A one-dimensional array has a rank of 1. We access the Rank property from the Array base class
.
Handle()
we test both 1D and 2D arrays in the same method. This method uses GetValue
to access the array elements.using System; class Program { static void Main() { // ... A one-dimensional array. int[] one = new int[2]; one[0] = 1; one[1] = 2; Handle(one); // ... A two-dimensional array. int[,] two = new int[2, 2]; two[0, 0] = 0; two[1, 0] = 1; two[0, 1] = 2; two[1, 1] = 3; Handle(two); } static void Handle(Array array) { Console.WriteLine("Rank: " + array.Rank); switch (array.Rank) { case 1: for (int i = 0; i < array.Length; i++) { Console.WriteLine(array.GetValue(i)); } break; case 2: for (int i = 0; i < array.GetLength(0); i++) { for (int x = 0; x < array.GetLength(1); x++) { Console.Write(array.GetValue(i, x)); } Console.WriteLine(); } break; } } }Rank: 1 1 2 Rank: 2 02 13
A jagged array is a single-dimensional array of other arrays. Because each element is another array, it can be used like a 2D array.
int
elements. The array has 3 rows—so it can have 3 subarrays once they are all assigned.int
arrays. We only have an array of empty references—new arrays must be created for the rows.Length
first on the array of references, and iterate through the rows.using System; // Part 1: declare local jagged array with 3 rows. int[][] jagged = new int[3][]; // Part 2: create a new array in the jagged array. jagged[0] = new int[2]; jagged[0][0] = 1; jagged[0][1] = 2; jagged[1] = new int[1]; jagged[2] = new int[3] { 3, 4, 5 }; // Part 3: iterate overall the elements. for (int i = 0; i < jagged.Length; i++) { // Part 4: iterate over the inner array. // ... Print all its elements. int[] innerArray = jagged[i]; for (int a = 0; a < innerArray.Length; a++) { Console.Write(innerArray[a] + " "); } Console.WriteLine(); }1 2 0 3 4 5
Here we see the use of a three-dimensional array. You can declare and initialize the multidimensional array using the comma syntax.
for
-loop allows it to handle greater dimensions.using System; // Create a three-dimensional array. int[, ,] threeDimensional = new int[3, 5, 4]; threeDimensional[0, 0, 0] = 1; threeDimensional[0, 1, 0] = 2; threeDimensional[0, 2, 0] = 3; threeDimensional[0, 3, 0] = 4; threeDimensional[0, 4, 0] = 5; threeDimensional[1, 1, 1] = 2; threeDimensional[2, 2, 2] = 3; threeDimensional[2, 2, 3] = 4; // Loop over each dimension's length. for (int i = 0; i < threeDimensional.GetLength(2); i++) { for (int y = 0; y < threeDimensional.GetLength(1); y++) { for (int x = 0; x < threeDimensional.GetLength(0); x++) { Console.Write(threeDimensional[x, y, i]); } Console.WriteLine(); } Console.WriteLine(); }100 200 300 400 500 000 020 000 000 000 000 000 003 000 000 000 000 004 000 000
GetUpperBound
loopGetUpperBound()
receives the highest index of the specified rank. It returns an int
. The word rank is the same concept as "dimension."
GetUpperBound
will work more reliably than other approaches.using System; string[,] codes = new string[,] { { "AA", "BB" }, { "CC", "DD" } }; // Get the upper bound. // ... Use for-loop over rows. for (int i = 0; i <= codes.GetUpperBound(0); i++) { string s1 = codes[i, 0]; string s2 = codes[i, 1]; Console.WriteLine("{0}, {1}", s1, s2); }AA, BB CC, DD
Length
-based loopThe fastest method for a 2D array is to do some arithmetic. In this example, there are five rows. GetUpperBound
(0) will return 4.
Length
, which is 10, and divide by 2, we get 5. We can iterate until we reach 5.using System; string[,] words = new string[,] { { "ONE", "TWO" }, { "THREE", "FOUR" }, { "FIVE", "SIX" } }; // Loop based on length. // ... Assumes each subarray is two elements long. for (int i = 0; i < words.Length / 2; i++) { string s1 = words[i, 0]; string s2 = words[i, 1]; Console.WriteLine("{0}, {1}", s1, s2); }ONE, TWO THREE, FOUR FIVE, SIX
GetUpperBound
, int
exampleHere we get the 2 dimensions of the array and iterate through them. We cache array bounds in locals for better performance and clarity.
GetUpperBound
can make an impact. But often, using jagged arrays (or flattened arrays) is the best solution.var
is often used with 2D arrays.using System; var codes = new int[,] { { 200, 400 }, { 2000, 4000 }, { 20000, 40000 } }; // Get all bounds before looping. int bound0 = codes.GetUpperBound(0); int bound1 = codes.GetUpperBound(1); // ... Loop over bounds. for (int i = 0; i <= bound0; i++) { for (int x = 0; x <= bound1; x++) { // Display the element at these indexes. Console.WriteLine("ELEMENT: {0}", codes[i, x]); } Console.WriteLine("--"); }ELEMENT: 200 ELEMENT: 400 -- ELEMENT: 2000 ELEMENT: 4000 -- ELEMENT: 20000 ELEMENT: 40000 --
We can create an empty 2D array by specifying its dimensions. All elements have the default value (for ints this is 0). When referring to a 2D array, the element type must match.
using System; // A two-dimensional array reference. int[,] array = new int[2, 2]; array[0, 0] = 1; Console.WriteLine(array[0, 0]); // The same reference can hold a different size of array. array = new int[3, 3]; array[2, 2] = 1; Console.WriteLine(array[2, 2]);1 1
A method may receive a 2D array by specifying the type of the elements. The dimensions are not used in the argument list—any 2D array of the correct element may be passed.
PrintFirstElement()
method receives a 2D bool
array. It then prints the value of the bool
in the first row and first column.using System; class Program { static void PrintFirstElement(bool[,] values) { // Display value of first element in first row. Console.WriteLine(values[0, 0]); } static void Main() { // Any array size of the right element type can be used. bool[,] values = new bool[100, 100]; values[0, 0] = true; PrintFirstElement(values); } }True
2D array loops are complicated. It is easy to cause errors related to invalid indexes. Here our 2D array is a four-element box, composed of two pairs.
for
-loop, we acquire the upper bound of the zero dimension, and the upper bound of the first dimension of the array.for
-loop.using System; // Instantiate a new 2D string array. string[,] array = new string[2, 2]; array[0, 0] = "top left"; array[0, 1] = "top right"; array[1, 0] = "bottom left"; array[1, 1] = "bottom right"; // Get upper bounds for the array int bound0 = array.GetUpperBound(0); int bound1 = array.GetUpperBound(1); // Use for-loops to iterate over the array elements. for (int variable1 = 0; variable1 <= bound0; variable1++) { for (int variable2 = 0; variable2 <= bound1; variable2++) { string value = array[variable1, variable2]; Console.WriteLine(value); } Console.WriteLine(); }top left top right bottom left bottom right
You cannot add a new row or column to a 2D array—the array is fixed in size and a new array must be created to add elements. Here we introduce AddRow
and AddColumn
.
AddRow
allocates a new 2D int
array and copies the current array into the new one. It then copies a separate int
array as a new row.AddColumn
allocates a new array with an extra column and copies the previous array. It copies an int
array into a new column.List
and adding int
arrays to that. Or add Lists of ints to a List
in a 2D list.using System; class Program { static int[,] AddRow(int[,] original, int[] added) { int lastRow = original.GetUpperBound(0); int lastColumn = original.GetUpperBound(1); // Create new array. int[,] result = new int[lastRow + 2, lastColumn + 1]; // Copy existing array into the new array. for (int i = 0; i <= lastRow; i++) { for (int x = 0; x <= lastColumn; x++) { result[i, x] = original[i, x]; } } // Add the new row. for (int i = 0; i < added.Length; i++) { result[lastRow + 1, i] = added[i]; } return result; } static int[,] AddColumn(int[,] original, int[] added) { int lastRow = original.GetUpperBound(0); int lastColumn = original.GetUpperBound(1); // Create new array. int[,] result = new int[lastRow + 1, lastColumn + 2]; // Copy the array. for (int i = 0; i <= lastRow; i++) { for (int x = 0; x <= lastColumn; x++) { result[i, x] = original[i, x]; } } // Add the new column. for (int i = 0; i < added.Length; i++) { result[i, lastColumn + 1] = added[i]; } return result; } static void Display(int[,] array) { // Loop over 2D int array and display it. for (int i = 0; i <= array.GetUpperBound(0); i++) { for (int x = 0; x <= array.GetUpperBound(1); x++) { Console.Write(array[i, x]); Console.Write(" "); } Console.WriteLine(); } } static void Main() { int[,] values = { { 10, 20 }, { 30, 40 } }; Console.WriteLine("CURRENT"); Display(values); // Add row and display the new array. int[,] valuesRowAdded = AddRow(values, new int[] { 50, 60 }); Console.WriteLine("ADD ROW"); Display(valuesRowAdded); // Add column and display the new array. int[,] valuesColumnAdded = AddColumn(valuesRowAdded, new int[] { -1, -2, -3 }); Console.WriteLine("ADD COLUMN"); Display(valuesColumnAdded); } }CURRENT 10 20 30 40 ADD ROW 10 20 30 40 50 60 ADD COLUMN 10 20 -1 30 40 -2 50 60 -3
A 2D array is not compatible with a 1D array—the types are different. So we cannot pass a 2D array to a method that accepts a 1D array (or the opposite case).
class Program { static void Main() { string[,] array = new string[10, 10]; Test(array); } static void Test(string[] example) { } }error CS1503: Argument 1: cannot convert from 'string[*,*]' to 'string[]'
GetUpperBound
GetUpperBound
is slow. Its result should be stored in a local variable. This benchmark shows the performance decrease with GetUpperBound
.
GetUpperBound
0 to loop over the rows in a 2D array. It stores the result of GetUpperBound
in a local.Length
divided by 2.Length
property for a loop boundary is faster than using GetUpperBound
.using System; using System.Diagnostics; using System.Runtime.CompilerServices; const int _max = 10000000; int[,] weights = new int[,] { { 100, 20 }, { 0, 0 }, { 5, 10 }, }; // Version 1: sum 2D array with GetUpperBound loop. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { int sum = 0; int top = weights.GetUpperBound(0); for (int z = 0; z <= top; z++) { sum += weights[z, 0] + weights[z, 1]; } if (sum != 135) { return; } } s1.Stop(); // Version 2: sum 2D array with Length-based loop. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { int sum = 0; int count = weights.Length / 2; for (int z = 0; z < count; z++) { sum += weights[z, 0] + weights[z, 1]; } if (sum != 135) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));9.34 ns GetUpperBound 6.98 ns Length / 2
With initializer syntax, we can create 2D arrays. And then with nested for
-loops we can iterate over them—it is easiest to use Length
, but GetUpperBound
is sometimes needed.