如何在c#中调整多维(2D)数组?

时间:2021-06-07 22:59:12

I tried the following but it just returns a screwed up array.

我尝试了下面的方法,但它只返回一个出错的数组。

    T[,] ResizeArray<T>(T[,] original, int rows, int cols)
    {
        var newArray = new T[rows,cols];
        Array.Copy(original, newArray, original.Length);
        return newArray;
    }

5 个解决方案

#1


33  

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 }

实际上是在内存中安排的:{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 }

内存中的布局如下:{1、2、3、0、4、5、6、0、0、0、0}

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

但数组。Copy将所有数组视为一维的。MSDN说:

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

{1、2、3、4、5、6、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.

这就是为什么你会得到一个混乱的数组…注意,如果更改行数,而不是列数,那么它将会工作。

#2


12  

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.

这结合了Thomas和Manuel的答案,并提供了数组的性能优势。复制和增加和减小数组大小的能力。

    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.

请注意,您的数组的x和y轴取决于您自己的实现,您可能需要切换0和1来达到预期的效果。

#3


8  

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.

它只会增加数组的大小,但它也可能会被调整以减少大小。

#4


1  

I was looking for something like this, but something that would effectively let me "pad" a 2D array from both ends, and also have the ability to reduce it.

我正在寻找这样的东西,但它能有效地让我从两端的2D数组中“填充”,并且有能力减少它。

I've run a very simple test: The array was string[1000,1000], average time for my machine was 44ms per resize. The resize increased or decreased padding on all sides by 1 each time, so all data in the array was copied. This performance hit was more than acceptable for my requirements.

我运行了一个非常简单的测试:数组是string[1000,1000],我的机器的平均时间是44ms / resize。每次都增加或减少所有边的填充,所以数组中的所有数据都被复制。这种性能的打击超出了我的要求。

public static void ResizeArray<T>(
    ref T[,] array, int padLeft, int padRight, int padTop, int padBottom)
{
    int ow = array.GetLength(0);
    int oh = array.GetLength(1);
    int nw = ow + padLeft + padRight;
    int nh = oh + padTop + padBottom;

    int x0 = padLeft;
    int y0 = padTop;
    int x1 = x0 + ow - 1;
    int y1 = y0 + oh - 1;
    int u0 = -x0;
    int v0 = -y0;

    if (x0 < 0) x0 = 0;
    if (y0 < 0) y0 = 0;
    if (x1 >= nw) x1 = nw - 1;
    if (y1 >= nh) y1 = nh - 1;

    T[,] nArr = new T[nw, nh];
    for (int y = y0; y <= y1; y++)
    {
        for (int x = x0; x <= x1; x++)
        {
            nArr[x, y] = array[u0 + x, v0 + y];
        }
    }
    array = nArr;
}

padLeft, padRight, padTop, padBottom can be negative or positive. If you pass in all 0s, the array generated will be identical to the source array.

padLeft, padRight, padTop, padBottom可以是负的或正的。如果您传入所有的0,所生成的数组将与源数组相同。

This could be particularly useful for anyone who wants to "scroll" their elements around their array.

这对于任何想要“滚动”其数组元素的人来说尤其有用。

Hope it's of use to someone!

希望它对某人有用!

#5


1  

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

#1


33  

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 }

实际上是在内存中安排的:{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 }

内存中的布局如下:{1、2、3、0、4、5、6、0、0、0、0}

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

但数组。Copy将所有数组视为一维的。MSDN说:

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

{1、2、3、4、5、6、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.

这就是为什么你会得到一个混乱的数组…注意,如果更改行数,而不是列数,那么它将会工作。

#2


12  

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.

这结合了Thomas和Manuel的答案,并提供了数组的性能优势。复制和增加和减小数组大小的能力。

    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.

请注意,您的数组的x和y轴取决于您自己的实现,您可能需要切换0和1来达到预期的效果。

#3


8  

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.

它只会增加数组的大小,但它也可能会被调整以减少大小。

#4


1  

I was looking for something like this, but something that would effectively let me "pad" a 2D array from both ends, and also have the ability to reduce it.

我正在寻找这样的东西,但它能有效地让我从两端的2D数组中“填充”,并且有能力减少它。

I've run a very simple test: The array was string[1000,1000], average time for my machine was 44ms per resize. The resize increased or decreased padding on all sides by 1 each time, so all data in the array was copied. This performance hit was more than acceptable for my requirements.

我运行了一个非常简单的测试:数组是string[1000,1000],我的机器的平均时间是44ms / resize。每次都增加或减少所有边的填充,所以数组中的所有数据都被复制。这种性能的打击超出了我的要求。

public static void ResizeArray<T>(
    ref T[,] array, int padLeft, int padRight, int padTop, int padBottom)
{
    int ow = array.GetLength(0);
    int oh = array.GetLength(1);
    int nw = ow + padLeft + padRight;
    int nh = oh + padTop + padBottom;

    int x0 = padLeft;
    int y0 = padTop;
    int x1 = x0 + ow - 1;
    int y1 = y0 + oh - 1;
    int u0 = -x0;
    int v0 = -y0;

    if (x0 < 0) x0 = 0;
    if (y0 < 0) y0 = 0;
    if (x1 >= nw) x1 = nw - 1;
    if (y1 >= nh) y1 = nh - 1;

    T[,] nArr = new T[nw, nh];
    for (int y = y0; y <= y1; y++)
    {
        for (int x = x0; x <= x1; x++)
        {
            nArr[x, y] = array[u0 + x, v0 + y];
        }
    }
    array = nArr;
}

padLeft, padRight, padTop, padBottom can be negative or positive. If you pass in all 0s, the array generated will be identical to the source array.

padLeft, padRight, padTop, padBottom可以是负的或正的。如果您传入所有的0,所生成的数组将与源数组相同。

This could be particularly useful for anyone who wants to "scroll" their elements around their array.

这对于任何想要“滚动”其数组元素的人来说尤其有用。

Hope it's of use to someone!

希望它对某人有用!

#5


1  

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