How to resize multidimensional (2D) array in C#?

Thank you Thomas, your explanation was very helpful but your implemented solution is too slow. I modified it to put Array.Copy to good use.

    void ResizeArray<T>(ref T[,] original, int newCoNum, int newRoNum)
    {
        var newArray = new T[newCoNum,newRoNum];
        int columnCount = original.GetLength(1);
        int columnCount2 = newRoNum;
        int columns = original.GetUpperBound(0);
        for (int co = 0; co <= columns; co++)
            Array.Copy(original, co * columnCount, newArray, co * columnCount2, columnCount);
        original = newArray;
    }

Here I'm assuming that there are more rows than columns so I structured the array as [columns, rows]. That way I use Array.Copy on an entire column in one shot (much faster than one cell a time).

It only works to increment the size of the array but it can probably be tweaked to reduce the size too.


Most methods in the array class only work with one-dimensional arrays, so you have to perform the copy manually:

T[,] ResizeArray<T>(T[,] original, int rows, int cols)
{
    var newArray = new T[rows,cols];
    int minRows = Math.Min(rows, original.GetLength(0));
    int minCols = Math.Min(cols, original.GetLength(1));
    for(int i = 0; i < minRows; i++)
        for(int j = 0; j < minCols; j++)
           newArray[i, j] = original[i, j];
    return newArray;
}

To understand why it doesn't work with Array.Copy, you need to consider the layout of a multidimensional array in memory. The array items are not really stored as a bidimensional array, they're stored contiguously, row after row. So this array:

{ { 1, 2, 3 },
  { 4, 5, 6 } }

Is actually arranged in memory like that: { 1, 2, 3, 4, 5, 6 }

Now, assume you want to add one more row and one more column, so that the array looks like this:

{ { 1, 2, 3, 0 },
  { 4, 5, 6, 0 },
  { 0, 0, 0, 0 } }

The layout in memory would now be as follows: { 1, 2, 3, 0, 4, 5, 6, 0, 0, 0, 0, 0 }

But Array.Copy treats all arrays as one-dimensional. MSDN says:

When copying between multidimensional arrays, the array behaves like a long one-dimensional array, where the rows (or columns) are conceptually laid end to end

So when you try to copy the original array to the new one, it just copies one memory location to the other, which gives, in one-dimensional representation:

{ 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0 }.

If you convert that to a two-dimensional representation, you get the following:

{ { 1, 2, 3, 4 },
  { 5, 6, 0, 0 },
  { 0, 0, 0, 0 } }

This is why you're getting a screwed up array... Note that it would work property if you changed the number of rows, but not the number of columns.


And for generic resizing of multi dimensional arrays:

public static class ArrayExtentions {
    public static Array ResizeArray(this Array arr, int[] newSizes) {
        if (newSizes.Length != arr.Rank) {
            throw new ArgumentException("arr must have the same number of dimensions as there are elements in newSizes", "newSizes");
        }

        var temp = Array.CreateInstance(arr.GetType().GetElementType(), newSizes);
        var sizesToCopy = new int[newSizes.Length];
        for (var i = 0; i < sizesToCopy.Length; i++) {
            sizesToCopy[i] = Math.Min(newSizes[i], arr.GetLength(i));
        }

        var currentPositions = new int[sizesToCopy.Length];
        CopyArray(arr, temp, sizesToCopy, currentPositions, 0);

        return temp;
    }

    private static void CopyArray(Array arr, Array temp, int[] sizesToCopy, int[] currentPositions, int dimmension) {
        if (arr.Rank - 1 == dimmension) {
            //Copy this Array
            for (var i = 0; i < sizesToCopy[dimmension]; i++) {
                currentPositions[dimmension] = i;
                temp.SetValue(arr.GetValue(currentPositions), currentPositions);
            }
        } else {
            //Recursion one dimmension higher
            for (var i = 0; i < sizesToCopy[dimmension]; i++) {
                currentPositions[dimmension] = i;
                CopyArray(arr, temp, sizesToCopy, currentPositions, dimmension + 1);
            }
        }
    }
}

This combines Thomas and Manuel's answers and provides the performance benefit of Array.Copy and the ability to both increase and decrease the size of the array.

    protected T[,] ResizeArray<T>(T[,] original, int x, int y)
    {
        T[,] newArray = new T[x, y];
        int minX = Math.Min(original.GetLength(0), newArray.GetLength(0));
        int minY = Math.Min(original.GetLength(1), newArray.GetLength(1));

        for (int i = 0; i < minY; ++i)
            Array.Copy(original, i * original.GetLength(0), newArray, i * newArray.GetLength(0), minX);

        return newArray;
    }

Please note that the x and y axis of your array is up to your own implementation and you may need to switch the 0s and 1s around to achieve the desired effect.

Tags:

C#

Arrays