繁体   English   中英

两种方式切割阵列的最快方法

[英]Fastest way to chop array in two pieces

我有一个数组,说:

var arr1 = new [] { 1, 2, 3, 4, 5, 6 };

现在,当我的数组大小超过5时,我想将当前数组的大小调整为3,并创建一个包含上3个值的新数组,因此在执行此操作之后:

arr1 = new [] { 1, 2, 3 };
newArr = new [] { 4, 5, 6 };

最快的方法是什么? 我想我将不得不调查非管理角落,但没有任何线索。


更多信息:

  • 阵列必须能够在没有大的性能命中的情况下进行调整
  • 这些数组只包含Int32
  • 数组的目的是在我的源数组中对数字进行分组,而不必对整个列表进行排序

简而言之:我想拆分以下输入数组:

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功能。 所以我想这会是最快的:

  1. 更改arr1所以它只包含3个项目
  2. 创建大小为3的新数组arr2
  3. 将不在arr1的字节重新导入到arr2

我不确定有什么比创建空数组更好,然后使用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>开始......如果没有更多的上下文,很难知道。

如果速度真的很关键,那么使用指针的非托管代码可能是最快的方法 - 但我肯定会检查你是否真的需要去冒险进入不安全的代码。

你在找这样的东西吗?

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();
}

这样就可以完全避免创建新的数组( 无法调整大小,甚至不能使用不安全的代码!),只需将指针传递给数组即可读取前三个整数的方法。

如果你的数组总是包含6个项目怎么样:

var newarr1 = new []{oldarr[0], oldarr[1],oldarr[2]};
var newarr2 = new []{oldarr[3], oldarr[4],oldarr[5]};

从内存中读取很快。

由于数组不是在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];
    }
}

注意:这可能不是完美的代码,这是一种实现方式的可怕方式,我绝不会在生产代码中这样做。

强制性LINQ解决方案:

if(arr1.Length > 5)
{
   var newArr = arr1.Skip(arr1.Length / 2).ToArray();
   arr1 = arr1.Take(arr1.Length / 2).ToArray();
}

LINQ比你想象的要快; 这将基本上受到框架在IEnumerable中旋转的能力的限制(在阵列上非常快)。 这应该在大致线性的时间内执行,并且可以接受任何初始大小的arr1。

暂无
暂无

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

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