C# 2D Array

Squares: 2D array

Data is sometimes two-dimensional. The C# language offers 2D arrays which are useful here. A 2D array is indexed with two numbers. It uses a special syntax form.
We can store any element type,
reference
or value.

Example

First, we use a two-dimensional string array. The first part (A) initializes a 2D array. And then parts B, C and D iterate over it. The example changes no values in the array. It just shows the indexing syntax.

String Arrays

Tip:To access a two-dimensional array element, please use the syntax array[0, 0]. Each dimension is indexed starting at zero.

Based on:

.NET 4.5

Program that uses two-dimensional arrays: C#

using System;

class Program
{
    static void Main()
    {
	// A. 2D array of strings.
	string[,] a = new string[,]
	{
	    {"ant", "aunt"},
	    {"Sam", "Samantha"},
	    {"clozapine", "quetiapine"},
	    {"flomax", "volmax"},
	    {"toradol", "tramadol"}
	};

	// B. Get the upper bound to loop.
	for (int i = 0; i <= a.GetUpperBound(0); i++)
	{
	    string s1 = a[i, 0]; // ant, Sam, clozapine...
	    string s2 = a[i, 1]; // aunt, Samantha, quetiapine...
	    Console.WriteLine("{0}, {1}", s1, s2);
	}
	Console.WriteLine();

	// C. Loop based on length.
	// ... Assumes each subarray is two elements long.
	for (int i = 0; i < a.Length / 2; i++)
	{
	    string s1 = a[i, 0];
	    string s2 = a[i, 1];
	    Console.WriteLine("{0}, {1}", s1, s2);
	}
	Console.WriteLine();

	// D. Get both bounds.
	int bound0 = a.GetUpperBound(0);
	int bound1 = a.GetUpperBound(1);
	for (int i = 0; i <= bound0; i++)
	{
	    for (int x = 0; x <= bound1; x++)
	    {
		string s1 = a[i, x];
		Console.WriteLine(s1);
	    }
	    Console.WriteLine();
	}
    }
}

Output

ant, aunt
Sam, Samantha
clozapine, quetiapine
flomax, volmax
toradol, tramadol

ant, aunt
Sam, Samantha
clozapine, quetiapine
flomax, volmax
toradol, tramadol

ant
aunt

Sam
Samantha

clozapine
quetiapine

flomax
volmax

toradol
tramadol

We declare a 2D array with string elements.
The syntax is somewhat confusing,
with curly brackets
and commas. You just need to memorize this. Here is what we see in the Visual Studio debugger.

Screenshot:The compiler sees the string[,] array as a string[5, 2] array. It automatically inferred the size.

2D array, locals in Visual Studio debugger

Using GetUpperBound. We next describe the usage of GetUpperBound in the example code. The GetUpperBound method receives the highest index of the specified rank (passed as an argument). This is probably not best for a simple 2D array.

Fragment that uses GetUpperBound: C#

for (int i = 0; i <= a.GetUpperBound(0); i++)
{
    string s1 = a[i, 0];
    string s2 = a[i, 1];
    Console.WriteLine("{0}, {1}", s1, s2);
}

Part C uses the array Length in its loop. The fastest method for a 2D array is to do some arithmetic. In this example, there are five rows. GetUpperBound(0) will return 4.
If we take Length,
which is 10,
and divide by 2,
we get 5.

Fragment that uses Length division: C#

for (int i = 0; i < a.Length / 2; i++)
{
    string s1 = a[i, 0];
    string s2 = a[i, 1];
    Console.WriteLine("{0}, {1}", s1, s2);
}

We cache array bounds in local variables for better performance and clarity. Part D above shows how to get the two dimensions of the array at runtime. We then iterate through those ranges.

Example 2

Two-dimensional: 2D

2D array loops are complicated. It is easy to cause errors related to invalid indexes. Here, we allocate a four-element two-dimensional string array on the managed heap. The 2D array is a four-element box, composed of two pairs.

Next:To begin our for-loop, we acquire the upper bound of the zero dimension, and the upper bound of the first dimension of the array.

Program that loops over 2D string array: C#

using System;

class Program
{
    static void Main()
    {
	// Instantiate a new two-dimensional 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();
	}
	Console.ReadLine();
    }
}

Output

top left
top right

bottom left
bottom right
Arrow indicates looping

In the program, the bounds of each dimension (rank) are equal to one. But if you modify the source array to have unequal dimensions, the for-loop will still correctly work. The for-loop is versatile. It works well in many programs.

For

Caution:The loop will not continue to work correctly if the array reference itself is modified or the array data is resized.

Also, nested loops are not always necessary. For example, if you are using a 2D array with only two elements in each row, you can index into positions 0 and 1 from a single for-loop. This is also more efficient.

Performance

Performance optimization

GetUpperBound is slow. You may not want to call it often. I took a benchmark comparing one million repetitions of B and C with the array shown. It shows the performance decrease with GetUpperBound.

Thus:Using the Length property for a loop boundary is faster than using GetUpperBound.

Jagged vs. 2D ArrayBenchmark
2D array benchmark result

Looping with GetUpperBound: 142 ms
Looping with Length/2:       47 ms

Rank

Property

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. It is sometimes helpful when dealing with an unknown array.

Array type

Here:We design a method (Handle) that receives an array reference. It then tests the Rank of the parameter array.

And:It handles both 1D and 2D arrays in the same method. Is uses GetValue to access the array elements.

Program that uses Rank: C#

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;
	}
    }
}

Output

Rank: 1
1
2
Rank: 2
02
13

Discussion

Squares: abstract

We can specify that a method receive a parameter of type two-dimensional array. Use the type with the comma syntax. The 2D array will be passed as a reference, which means changes to it will also affect the original version.

Note:The reference itself will be copied, but not the data to which it points.

Dimensions. The C# language also allows you to specify arrays with more than two dimensions. This capability is not explored here. We can use another comma in the indexing syntax and it will work as expected.

Multidimensional Array

Lists. You can use the generic type List to simulate a jagged or 2D List that is dynamically resizable. The syntax for this nested type is somewhat more confusing. And internally, it is implemented similarly to jagged arrays.

Tip:This can solve problems that would otherwise require confusing 2D array resizing and copying.

Nested List

Research

Books

In the CLI standard, I found material about the types of arrays. The Framework determines the type of an array by its rank (dimension count) and its element type. So both parts are considered in determining type.

CLI Standard

The rank of an array is the number of dimensions. The type of an array (other than a vector) shall be determined by the type of its elements and the number of dimensions. Miller & Ragsdale, p. 238

Summary

Piece of computer software: a program

2D arrays have many uses. And they can be used in many ways: we showed several indexing approaches. It is easiest to use Length, but GetUpperBound is sometimes needed. We used the comma syntax. We further benchmarked these arrays.

Tip:Before you use 2D arrays, research jagged arrays. These can further improve the clarity and performance of your code.

Jagged Arrays

C#: Array