如何在C#中截断数组

How to truncate an array in place in C#

本文关键字:数组      更新时间:2023-10-16

我的意思是这真的可能吗?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方法不适用于您的情况的测量?)