如何在C#中调整多维(2D)数组的大小
本文关键字:2D 数组 调整 | 更新日期: 2023-09-27 18:00:53
我尝试了以下操作,但它只是返回了一个错误的数组。
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
一起使用,您需要考虑内存中多维数组的布局。数组项并不是作为二维数组存储的,而是连续存储的,一行接一行。所以这个数组:
{ { 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表示:
在多维数组之间进行复制时,数组的行为类似于一个长的一维数组,其中行(或列(在概念上是首尾相连的
因此,当你试图将原始数组复制到新数组时,它只会将一个内存位置复制到另一个,这在一维表示中给出:
{ 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0 }
。
如果你将其转换为二维表示,你会得到以下内容:
{ { 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;
}
请注意阵列的x轴和y轴取决于您自己的实现,您可能需要切换0和1以获得所需效果。
谢谢Thomas,您的解释很有帮助,但您实现的解决方案太慢了。我修改了它以放置数组。复制以充分利用它。
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;
}
这里我假设行多于列,所以我将数组构造为[columns,rows]。这样我就使用了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);
}
}
}
}
我一直在寻找这样的东西,但它可以有效地让我从两端"填充"2D阵列,并有能力减少它。
我做了一个非常简单的测试:数组是字符串[100001000],我的机器每次调整大小的平均时间是44ms。每次调整大小都会在所有边上增加或减少1的填充,因此数组中的所有数据都会被复制。对于我的要求来说,这种性能上的冲击是完全可以接受的。
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,则生成的数组将与源数组相同。
这对于任何想要在数组中"滚动"元素的人来说都特别有用。
希望对别人有用!
这建立在Manuel的答案之上,还允许对复制的数据应用偏移量(例如,将源数组复制到目标数组的中心,而不是[0,0](:
public static T[,] ResizeArray<T>(T[,] original, int newWidth, int newHeight, int offsetX = 0, int offsetY = 0)
{
T[,] newArray = new T[newWidth, newHeight];
int width = original.GetLength(0);
int height = original.GetLength(1);
for (int x = 0; x < width; x++) {
Array.Copy(original, x * height, newArray, (x + offsetX) * newHeight + offsetY, height);
}
return newArray;
}
我真的很喜欢Stephen Tierney在他人工作的基础上给出的答案。正如Stephen所指出的,x/y由解释决定。
我对它进行了轻微的重构,以使用m*n大小&ij坐标索引可能是矩阵最常见的表示法(参考维基百科https://en.wikipedia.org/wiki/Matrix_(数学((。
在这种表示法中,
- "m"是行数
- "n"是列数
- "i"是第一个坐标,也就是行索引(随着向下而增加(
public static T[,] Resize2D<T>(this T[,] original, int m, int n)
{
T[,] newArray = new T[m, n];
int mMin = Math.Min(original.GetLength(0), newArray.GetLength(0));
int nMin = Math.Min(original.GetLength(1), newArray.GetLength(1));
for (int i = 0; i < mMin; i++)
Array.Copy(original, i * original.GetLength(1), newArray, i * newArray.GetLength(1), nMin);
return newArray;
}