简体   繁体   English

如何在C#中截断数组

[英]How to truncate an array in place in C#

I mean is it really possible? 我的意思是真的有可能吗? MSDN says that arrays are fixed-size and the only way to resize is "copy-to-new-place". MSDN说,阵列是固定大小的,唯一调整大小的方法是“复制到新位置”。 But maybe it is possible with unsafe/some magic with internal CLR structures, they all are written in C++ where we have a full memory control and can call realloc and so on. 但是,使用内部CLR结构的不安全/某种魔术可能是有可能的,它们都是用C ++编写的,在C ++中,我们拥有完整的内存控制,并且可以调用realloc等。

I have no code provided for this question, because I don't even know if it can exist. 我没有为此问题提供代码,因为我什至不知道它是否可以存在。


I'm not talking about Array.Resize methods and so on, because they obviosly do not have needed behaviour. 我不是在谈论Array.Resize方法等等,因为它们显然不需要行为。

Assume that we have a standard x86 process with 2GB ram, and I have 1.9GB filled by single array. 假设我们有一个带有2GB内存的标准x86进程,并且我有1.9GB的单个阵列已填充。 Then I want to release half of it. 然后我要释放一半。 So I want to write something like: 所以我想写一些类似的东西:

MagicClass.ResizeArray(ref arr, n)

And do not get OutOfMemoryException. 并且不要得到OutOfMemoryException。 Array.Resize will try to allocate another gigabyte of RAM and will fail with 1.9+1 > 2GB OutOfMemory. Array.Resize将尝试分配另一GB的RAM,并且将因1.9 + 1> 2GB OutOfMemory而失败。

You can try Array.Resize() : 您可以尝试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 will attempt to do the inplace resize - but it reserves the right to copy the whole thing elsewhere and return a pointer that's completely different. realloc尝试进行就地调整大小-但它保留将整个内容复制到其他位置并返回完全不同的指针的权利。

Pretty much the same outward behaviour is exposed by .NET's List<T> class - which you should be using anyway if you find yourself changing array sizes often. .NET的List<T>类暴露了几乎相同的向外行为-如果您发现自己经常更改数组大小,则无论如何都应该使用它。 It hides the actual array reference from you so that the change is propagated throughout all of the references to the same list. 它对您隐藏了实际的数组引用,因此更改会在所有引用中传播到同一列表。 As you remove items from the end, only the length of the list changes while the inner array stays the same - avoiding the copying. 当您从末尾删除项目时,只有列表的长度发生变化,而内部数组保持不变-避免了复制。

It doesn't release the memory (you can always do that explicitly with Capacity = XXX , but that makes a new copy of the array), but then again, unless you're working with large arrays, neither does realloc - and if you're working with large arrays, yada, yada - we've been there :) 它不会释放内存(您始终可以使用Capacity = XXX显式地执行该操作,但是会创建该数组的新副本),然后再次声明,除非您使用的是大型数组,否则都不会重新realloc -如果您正在使用大型阵列,yada,yada-我们去过那里:)

realloc doesn't really make sense in the kind of memory model .NET has anyway - the heap is continously collected and compacted over time. 在.NET拥有的内存模型中, realloc并没有真正意义-随着时间的推移,将不断收集和压缩堆。 So if you're trying to use it to avoid the copies when just trimming an array, while also keeping memory usage low... don't bother. 因此,如果您只是在修剪数组时尝试使用它来避免出现副本,同时又要保持较低的内存使用量,请不要打扰。 At the next heap compaction, the whole memory above your array is going to be moved to fill in the blanks. 在下一次堆压缩时,将移动数组上方的整个内存以填补空白。 Even if it were possible to do the realloc , the only benefit you have over simply copying the array is that you would keep your array in the old-living heap - and that isn't necessarily what you want anyway. 即使可以进行realloc ,相比简单地复制数组,您唯一的好处就是可以将数组保留在旧的堆中-但这不一定是您想要的。

Neither array type in BCL supports what you want. BCL中的两种数组类型都不支持您想要的。 That being said - you can implement your own type that would support what you need. 话虽这么说-您可以实现自己的类型来支持您的需求。 It can be backed by standard array, but would implement own Length and indexer properties, that would 'hide' portion of array from you. 它可以由标准数组支持,但是会实现自己的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");
        }
    }
}

It really depend what exactly do need. 这实际上取决于确切需要什么。 (Eg do you need to truncate just so that you can easier use it from your code. Or is perf/GC/memory consumption a concern? If the latter is the case - did you perform any measurements that proves standard Array.Resize method unusable for your case?) (例如,您是否需要截断,以便可以从代码中更轻松地使用它。或者是否需要关注perf / GC /内存消耗?如果是这种情况,那么是否执行了任何测量,证明标准Array.Resize方法不可用为你的情况?)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM