我有一个阵列,说:
var arr1 = new [] { 1, 2, 3, 4, 5, 6 };
现在,当我的数组大小超过5时,我想将当前数组的大小调整为3,并创建一个包含上3个值的新数组,因此在执行此操作后:
arr1 = new [] { 1, 2, 3 };
newArr = new [] { 4, 5, 6 };
最快的方法是什么?我想我将不得不调查未管理的角落,但没有任何线索。
更多信息:
简而言之:我想拆分以下输入数组:
int[] arr = new int[] { 1, 3, 4, 29, 31, 33, 35, 36, 37 };
到
arr1 = 1, 3, 4
arr2 = 29, 31, 33, 35, 36, 37
但由于数组大小为3时达到理想速度,arr2
应分成2个大小相等的数组。
注意
我知道数组在内存中的实现非常幼稚(好吧,至少在C中,你可以操作数组中的项目数,以便数组调整大小)。另外,Win32 API中的某个地方有一个memory move
函数。所以我想这将是最快的:
arr1
,使其仅包含3个项目arr2
arr1
以外的字节移至arr2
答案 0 :(得分:6)
我不确定有什么比创建空数组更好,然后使用Array.Copy
。我至少希望在内部进行优化:)
int[] firstChunk = new int[3];
int[] secondChunk = new int[3];
Array.Copy(arr1, 0, firstChunk, 0, 3);
Array.Copy(arr1, 3, secondChunk, 0, 3);
老实说,对于非常小的数组,方法调用的开销可能比仅显式分配元素更大 - 但我认为实际上你会使用稍大的数组:)
您可能还会考虑不实际拆分数组,而是使用ArraySegment
来分别拥有数组的“块”。或者也许使用List<T>
开始......如果没有更多的背景,很难知道。
如果速度真的关键,那么使用指针的非托管代码可能是最快的方法 - 但我肯定会检查你是否真的需要去冒险进入不安全的代码之前。
答案 1 :(得分:4)
你在找这样的东西吗?
static unsafe void DoIt(int* ptr)
{
Console.WriteLine(ptr[0]);
Console.WriteLine(ptr[1]);
Console.WriteLine(ptr[2]);
}
static unsafe void Main()
{
var bytes = new byte[1024];
new Random().NextBytes(bytes);
fixed (byte* p = bytes)
{
for (int i = 0; i < bytes.Length; i += sizeof(int))
{
DoIt((int*)(p + i));
}
}
Console.ReadKey();
}
这样可以完全避免创建新数组(不能调整大小,甚至不安全代码!),只是将指针传递给数组,读取前三个整数的方法。
答案 2 :(得分:2)
如果你的数组总是包含6个项目:
var newarr1 = new []{oldarr[0], oldarr[1],oldarr[2]};
var newarr2 = new []{oldarr[3], oldarr[4],oldarr[5]};
从内存中读取很快。
答案 3 :(得分:0)
由于数组未在C#中动态调整大小,这意味着您的第一个数组的最小长度必须为5或最大长度为6,具体取决于您的实现。然后,每次需要拆分时,您将不得不动态创建3个新的静态大小的数组。只有在每次拆分后,您的数组项才会按自然顺序排列,除非您将每个新数组的长度设置为5或6,并且仅添加到最新数组。这种方法意味着每个新数组也会有2-3个额外的指针。
除非你在编译应用程序之前有一个已知数量的项目进入你的数组,你也必须为动态创建的数组提供某种形式的持有者,这意味着你将不得不拥有一个数组数组(锯齿状数组)。由于您的锯齿状数组也是静态大小的,因此在实例化每个新动态创建的数组时,您需要能够动态地重新创建它并调整其大小。
我会说将这些项目复制到新阵列中是您最不担心的问题。你正在寻找一些非常大的性能命中以及数组大小的增长。
更新:所以,如果我绝对需要......
public class MyArrayClass
{
private int[][] _master = new int[10][];
private int[] _current = new int[3];
private int _currentCount, _masterCount;
public void Add(int number)
{
_current[_currentCount] = number;
_currentCount += 1;
if (_currentCount == _current.Length)
{
Array.Copy(_current,0,_master[_masterCount],0,3);
_currentCount = 0;
_current = new int[3];
_masterCount += 1;
if (_masterCount == _master.Length)
{
int[][] newMaster = new int[_master.Length + 10][];
Array.Copy(_master, 0, newMaster, 0, _master.Length);
_master = newMaster;
}
}
}
public int[][] GetMyArray()
{
return _master;
}
public int[] GetMinorArray(int index)
{
return _master[index];
}
public int GetItem(int MasterIndex, int MinorIndex)
{
return _master[MasterIndex][MinorIndex];
}
}
注意:这可能不是完美的代码,这是一种实现方式的可怕方式,我绝不会在生产代码中这样做。
答案 4 :(得分:-1)
强制性LINQ解决方案:
if(arr1.Length > 5)
{
var newArr = arr1.Skip(arr1.Length / 2).ToArray();
arr1 = arr1.Take(arr1.Length / 2).ToArray();
}
LINQ比你想象的要快;这将基本上受到框架在IEnumerable中旋转的能力的限制(在阵列上非常快)。这应该在大致线性的时间内执行,并且可以接受任何初始大小的arr1。