不使用ToArray()将IEnumerable <int>转换为int []

时间:2016-03-10 18:36:35

标签: c# ienumerable toarray

如何在没有IEnumerable<int>的情况下将int[]转换为ToArray()?我需要一种比ToArray()更快的方法或功能。 我有一个大的int大型状态和两个小的大型使用方法的Take和Skip。代码:

 int[] left = state.Take(pivot).ToArray();
 int[] right = state.Skip(pivot).ToArray();

pivot - 分裂点。

2 个答案:

答案 0 :(得分:6)

有四种方法可以从int[]获得IEnumerable<int>。按速度顺序:

  1. 它已经是一个int[],然后将其丢回。
  2. 这是一个拥有自己的CountCopyTo的集合类型,使用它。
  3. 这是一种类型,您可以找到它的大小,分配该大小的数组,并通过迭代源来填充它。
  4. 分配一个数组,填充它但是如果你到达终点然后重新分配一个新数组并复制。继续这样做,直到完成,然后在必要时修剪数组。
  5. Linq的ToArray()不会做第一个,因为ToArray()旨在保护源免受ToArray()结果的更改。

    其余的,Linq试图选择最快的选择。因此,你不会有太大的进步。

    如果您可以接受投射到数组,那么您将能够用以下内容覆盖该情况:

    int[] array = intEnum as int[] ?? intEnum.ToArray();
    

    (如果你知道intEnum总是一个数组,那么你可以直接进行强制转换。相反,如果你知道intEnum永远不是一个数组,那么上面的速度会慢一点,因为它总是有没有获得实现该方法的好处的尝试成本。)

    否则,虽然你不能做得更好,除非你知道可枚举的大小,但它不是ICollection<int>所以linq不能这样做找到它自己。

    修改

    评论中添加了评论:

      

    我有一个大的int数组。通过方法的Take and Skip,我把它分成两个大块。

    如果您正在使用.NET Core,那么这个案例已经针对上个月合并到祝福存储库中的提交进行了优化,因此如果您使用最新版本的corefx或等待更新,那么您需要&#39已经得到了这个优化。

    否则,您可以应用与Linq版本相同的逻辑:

    让我们调用我们的源数组sourceArray

    我们有IEnumerable<int>

    之类的var en = sourceArray.Skip(amountSkipped).Take(amountTaken);

    如果amountSkippedamountTaken小于零,则此处应使用零。如果省略Skip(),那么amountSkipped应该使用零。如果遗漏了Take(),则应int.MaxValue使用amountTaken

    输出数组的大小为:

    int count = Math.Min(sourceArray.Length - amountSkipped, amountTaken);
    

    然后我们可以创建并分配到一个数组:

    int[] array = new int[count];
    int index = 0;
    foreach (int item in en)
      array[index] = item;
    

    现在array将被填充而无需分配和修剪,大约节省了log count次分配。这确实会更快。

    同样,如果您使用最新的corefx,那么这已经为您完成了,并进一步优化了能够避免枚举器分配。

    尽管如此,如果所做的一切都是SkipTake,那么你可以放弃它并直接操作:

    int count = Math.Min(sourceArray.Length - amountSkipped, amountTaken);
    int[] array = new int[count];
    Array.Copy(sourceArray, amountSkipped, array, 0, count);
    

    根本不需要Skip()Take()

    再次修改

    现在的问题是:

    int[] left = state.Take(pivot).ToArray();
    int[] right = state.Skip(pivot).ToArray();
    

    我们可以简单地做到这一点:

    int leftCount = Math.Min(state.Length, Math.Max(pivot, 0));
    int[] left = new int[leftCount];
    Array.Copy(state, 0, left, 0, leftCount);
    
    int rightCount = Math.Min(state.Length - leftCount);
    int[] right = new int[rightCount];
    Array.Copy(state, leftCount, right, 0, rightCount);
    

    这确实是一个相当大的进步。

    如果我们知道pivot介于0和state.Length之间,那么我们可以使用更简单的方法:

    int[] left = new int[pivot];
    Array.Copy(state, 0, left, 0, pivot);
    
    int[] right = new int[state.Length - pivot];
    Array.Copy(state, pivot, right, 0, state.Length - pivot);
    

答案 1 :(得分:1)

这在很大程度上取决于IEnumerable背后的实际类型。如果它已经是一个数组,你可以投射它。如果是其他任何东西,除了ToArray()之外通常无法做到这一点。您可能想要考虑为什么它很慢。也许下面有一个LINQ查询可以进行数据库调用,可以进行优化