如何在C#中调整多维(2D)数组?
我尝试了以下,但它只是返回一个搞砸的数组。
T[,] ResizeArray<T>(T[,] original, int rows, int cols) { var newArray = new T[rows,cols]; Array.Copy(original, newArray, original.Length); return newArray; }
数组中的大多数方法只能用于一维数组,因此您必须手动执行复制:
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; }
要理解为什么它不能与Array.Copy
工作,您需要考虑内存中multidimensional array的布局。 数组项不是真正的存储为二维数组,它们连续存储,一排一排地存储。 所以这个数组:
{ { 1, 2, 3 }, { 4, 5, 6 } }
实际上是这样安排在记忆中: { 1, 2, 3, 4, 5, 6 }
现在,假设您想要添加一行多一列,以便数组看起来像这样:
{ { 1, 2, 3, 0 }, { 4, 5, 6, 0 }, { 0, 0, 0, 0 } }
现在内存中的布局如下: { 1, 2, 3, 0, 4, 5, 6, 0, 0, 0, 0, 0 }
但Array.Copy
将所有数组视为一维。 MSDN说:
当在multidimensional array之间进行复制时,数组的行为就像一个长的一维数组,其中行(或列)在概念上是端对端
因此,当您尝试将原始数组复制到新数组中时,它只是将一个内存位置复制到另一个内存位置,从而以一维表示forms提供:
{ 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0 }
。
如果将其转换为二维表示forms,则会得到以下结果:
{ { 1, 2, 3, 4 }, { 5, 6, 0, 0 }, { 0, 0, 0, 0 } }
这就是为什么你得到一个搞砸的数组…注意,如果你改变了行数而不是列数,它将工作。
这结合了Thomas和Manuel的答案,并提供了Array.Copy的性能优势以及增加和减小数组大小的能力。
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; }
请注意 ,arrays的x和y轴取决于您自己的实现,您可能需要切换0和1以达到所需的效果。
谢谢你,托马斯,你的解释是非常有用的,但你实施的解决scheme太慢了。 我修改它把Array.Copy好用。
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; }
在这里,我假设有更多的行比列,所以我把数组结构为[列,行]。 这样一次在整个列上使用Array.Copy(比一次更快)。
它只能增加数组的大小,但也可能会调整以减小大小。
我正在寻找这样的东西,但是有些东西会让我从两端“垫”一个二维数组,并且也有能力减less它。
我运行了一个非常简单的testing:数组是string[1000,1000],我的机器平均每次resize为44ms。 每次resize增加或减less1的填充,所以数组中的所有数据都被复制。 这个性能比我的要求更可接受。
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可以是负值也可以是负值。 如果你全部传入0,则生成的数组将与源数组相同。
对于任何想要围绕arrays“滚动”元素的人来说,这可能特别有用。
希望对某人有用!
对于multidimensional array的通用大小调整:
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); } } } }