在没有 ToArray() 的情况下将 IEnumerable<int> 转换为 int[]
Convert IEnumerable<int> to int[] without ToArray()
如何在没有 ToArray()
的情况下将 IEnumerable<int>
转换为 int[]
?我需要一种比 ToArray()
运行速度更快的方法或函数。
我有一个 big int massive state 和两个 small massive left 和 right 使用方法的 Take 和 Skip。代码:
int[] left = state.Take(pivot).ToArray();
int[] right = state.Skip(pivot).ToArray();
枢轴 - 分割点。
这在很大程度上取决于 IEnumerable 背后的实际类型。如果它已经是一个数组,你可以转换它。如果是其他任何事情,除了 ToArray()
之外通常没有办法做到这一点。你可能想考虑为什么它很慢。也许下面有一个 LINQ 查询执行可以优化的数据库调用
有四种可能的方法可以从 IEnumerable<int>
得到 int[]
。按速度排序:
- 它已经是一个
int[]
,把它扔回去。
- 它是一个集合类型,有自己的
Count
和 CopyTo
,请使用它。
- 这是一种类型,您可以找到其大小,分配该大小的数组,并通过遍历源来填充它。
- 分配一个数组,填充它,但如果到达末尾,则重新分配一个新数组并复制过来。继续这样做直到完成,然后 trim 数组(如有必要)。
Linq 的 ToArray()
不会首先执行,因为 ToArray()
旨在保护来源免受 ToArray()
.
结果的更改
在其余选项中,Linq 尝试选择最快的选项。因此,您不会得到太大的改善。
如果转换为数组对您来说是可以接受的,那么您将能够涵盖这种情况:
int[] array = intEnum as int[] ?? intEnum.ToArray();
(如果您知道 intEnum
始终是一个数组,那么您可以直接进行转换。相反,如果您知道 intEnum
永远不是一个数组,那么上面的代码会更慢,因为它总是要付出尝试的代价,却从未从实现该方法中获益。
否则,虽然你无法做得更好,除非你知道可枚举的大小,但它不是 ICollection<int>
所以 linq 找不到它本身。
编辑:
添加到问题的评论:
i have one big int array. By method's Take and Skip I divide it into two massive.
如果您使用的是 .NET Core,那么这个案例已经针对上个月合并到 blessed 存储库中的提交进行了优化,因此如果您使用最新版本的 corefx 或等待更新,那么您已经已经优化过了。
否则,您可以应用与该版本的 Linq 相同的逻辑:
让我们称我们的源数组为sourceArray
。
我们有一个 IEnumerable<int>
来自 var en = sourceArray.Skip(amountSkipped).Take(amountTaken);
如果 amountSkipped
或 amountTaken
小于零,则此处应使用零。如果 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
无需分配和 trim 即可填充,大约节省 log count
分配。这样确实会更快。
同样,如果您使用的是最新的 corefx,那么这已经为您完成,并进一步优化能够避免枚举器分配。
不过,如果所做的只是 Skip
和 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);
如何在没有 ToArray()
的情况下将 IEnumerable<int>
转换为 int[]
?我需要一种比 ToArray()
运行速度更快的方法或函数。
我有一个 big int massive state 和两个 small massive left 和 right 使用方法的 Take 和 Skip。代码:
int[] left = state.Take(pivot).ToArray();
int[] right = state.Skip(pivot).ToArray();
枢轴 - 分割点。
这在很大程度上取决于 IEnumerable 背后的实际类型。如果它已经是一个数组,你可以转换它。如果是其他任何事情,除了 ToArray()
之外通常没有办法做到这一点。你可能想考虑为什么它很慢。也许下面有一个 LINQ 查询执行可以优化的数据库调用
有四种可能的方法可以从 IEnumerable<int>
得到 int[]
。按速度排序:
- 它已经是一个
int[]
,把它扔回去。 - 它是一个集合类型,有自己的
Count
和CopyTo
,请使用它。 - 这是一种类型,您可以找到其大小,分配该大小的数组,并通过遍历源来填充它。
- 分配一个数组,填充它,但如果到达末尾,则重新分配一个新数组并复制过来。继续这样做直到完成,然后 trim 数组(如有必要)。
Linq 的 ToArray()
不会首先执行,因为 ToArray()
旨在保护来源免受 ToArray()
.
在其余选项中,Linq 尝试选择最快的选项。因此,您不会得到太大的改善。
如果转换为数组对您来说是可以接受的,那么您将能够涵盖这种情况:
int[] array = intEnum as int[] ?? intEnum.ToArray();
(如果您知道 intEnum
始终是一个数组,那么您可以直接进行转换。相反,如果您知道 intEnum
永远不是一个数组,那么上面的代码会更慢,因为它总是要付出尝试的代价,却从未从实现该方法中获益。
否则,虽然你无法做得更好,除非你知道可枚举的大小,但它不是 ICollection<int>
所以 linq 找不到它本身。
编辑:
添加到问题的评论:
i have one big int array. By method's Take and Skip I divide it into two massive.
如果您使用的是 .NET Core,那么这个案例已经针对上个月合并到 blessed 存储库中的提交进行了优化,因此如果您使用最新版本的 corefx 或等待更新,那么您已经已经优化过了。
否则,您可以应用与该版本的 Linq 相同的逻辑:
让我们称我们的源数组为sourceArray
。
我们有一个 IEnumerable<int>
来自 var en = sourceArray.Skip(amountSkipped).Take(amountTaken);
如果 amountSkipped
或 amountTaken
小于零,则此处应使用零。如果 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
无需分配和 trim 即可填充,大约节省 log count
分配。这样确实会更快。
同样,如果您使用的是最新的 corefx,那么这已经为您完成,并进一步优化能够避免枚举器分配。
不过,如果所做的只是 Skip
和 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);