-5

我怎样才能转换IEnumerable<int>int[]没有ToArray()?我需要一种比ToArray(). 我使用方法的 Take and Skip有一个大的 int 大规模状态和两个小的大规模左右。代码:

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

枢轴 - 分割点。

4

2 回答 2

7

有四种可能的方法可以int[]IEnumerable<int>. 按速度排序:

  1. 它已经是一个int[],把它扔回去。
  2. 它是一个有自己的集合类型CountCopyTo使用它。
  3. 它是一种类型,您可以找到它的大小,分配该大小的数组,并通过遍历源来填充它。
  4. 分配一个数组,填充它,但如果你到达末尾,然后重新分配一个新数组并复制过来。继续这样做直到你完成,然后在必要时修剪阵列。

LinqToArray()不会做第一个,因为ToArray()它是为了保护源代码免受ToArray().

在其余部分中,Linq 尝试选择最快的选项。因此,您不会得到太大的改进。

如果您可以接受强制转换为数组,那么您将能够通过以下方式解决这种情况:

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

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

否则,虽然您将无法做得更好,除非您知道可枚举的大小,但它不是ICollection<int>这样的 linq 无法找到它本身。

编辑

在问题中添加了一条评论:

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

如果您使用的是 .NET Core,那么这种情况已经针对上个月合并到祝福存储库中的提交进行了优化,因此如果您使用最新版本的 corefx 或等待更新,那么您已经对此进行了优化。

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

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

我们有一个类似的IEnumerable<int>东西var en = sourceArray.Skip(amountSkipped).Take(amountTaken);

如果amountSkippedamountTaken小于零,则此处应使用零。如果Skip()被忽略了,那么 0 应该被用于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,那么这已经为您完成了,进一步优化能够避免枚举器分配。

尽管如此,如果所做的一切都是 toSkip然后Take你可以完全放弃它并直接操作:

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);
于 2016-03-10T18:51:12.640 回答
1

这很大程度上取决于 IEnumerable 背后的实际类型。如果它已经是一个数组,你可以转换它。如果是其他任何事情,通常除了ToArray(). 您可能要考虑为什么它很慢。也许下面有一个 LINQ 查询可以进行数据库调用,可以优化

于 2016-03-10T18:46:13.900 回答