如何在C#中截断数组
本文关键字:数组 | 更新日期: 2023-09-27 18:19:51
我的意思是这真的可能吗?MSDN表示,数组是固定大小的,调整大小的唯一方法是"复制到新位置"。但也许内部CLR结构有一些不安全/魔力,它们都是用C++编写的,我们有一个完整的内存控制,可以调用realloc
等等
我没有提供这个问题的代码,因为我甚至不知道它是否存在。
我不是在谈论Array.Resize方法等等,因为它们显然没有所需的行为。
假设我们有一个2GB ram的标准x86进程,而我有1.9GB由单个阵列填充。然后我想发布一半。所以我想写一些类似的东西:
MagicClass.ResizeArray(ref arr, n)
并且不要获取OutOfMemoryException。Array.Resize将尝试分配另一个GB的RAM,并将失败,出现1.9+1>2GB的OutOfMemory。
您可以尝试Array.Resize()
:
int[] myArray = new int[] { 1, 2, 3, 4 };
int myNewSize = 1;
Array.Resize(ref myArray, myNewSize);
// Test: 1
Console.Write(myArray.Length);
realloc
将尝试进行就地调整大小,但它保留将整个内容复制到其他地方并返回完全不同的指针的权利。
.NET的List<T>
类暴露了几乎相同的向外行为——如果您发现自己经常更改数组大小,那么无论如何都应该使用它。它向您隐藏实际的数组引用,以便更改在同一列表的所有引用中传播。当您从末尾删除项时,只有列表的长度会发生变化,而内部数组保持不变,从而避免了复制。
它不会释放内存(您总是可以用Capacity = XXX
显式地这样做,但这会生成阵列的新副本),但话说回来,除非您使用大型阵列,否则realloc
也不会释放内存,如果您使用大型数组,yada,yada-我们已经做到了:)
无论如何,realloc
在.NET的内存模型中是没有意义的——堆是随着时间的推移不断收集和压缩的。因此,如果你试图在修剪数组时使用它来避免副本,同时保持低内存使用率。。。别麻烦了。在下一次堆压缩时,数组上方的整个内存将被移动以填补空白。即使可以执行realloc
,与简单地复制数组相比,唯一的好处是可以将数组保留在旧的活动堆中,而这并不一定是您想要的。
BCL中的两种数组类型都不支持您想要的。话虽如此,你可以实现自己的类型,以支持你所需要的。它可以由标准数组支持,但将实现自己的Length和indexer属性,这将对您"隐藏"数组的一部分。
public class MyTruncatableArray<T>
{
private T[] _array;
private int _length;
public MyTruncatableArray(int size)
{
_array = new T[size];
_length = size;
}
public T this[int index]
{
get
{
CheckIndex(index, _length);
return _array[index];
}
set
{
CheckIndex(index, _length);
_array[index] = value;
}
}
public int Length
{
get { return _length; }
set
{
CheckIndex(value);
_length = value;
}
}
private void CheckIndex(int index)
{
this.CheckIndex(index, _array.Length);
}
private void CheckIndex(int index, int maxValue)
{
if (index < 0 || index > maxValue)
{
throw new ArgumentException("New array length must be positive and lower or equal to original size");
}
}
}
这真的取决于你到底需要什么。(例如,您是否需要截断,以便更容易地从代码中使用它。或者perf/GC/内存消耗是一个问题吗?如果是后者,您是否执行了任何证明标准Array.Resize方法不适用于您的情况的测量?)