How can I convert IEnumerable<int>
to int[]
without ToArray()
? I need a method or function that will work faster than ToArray()
.
I have one big int massive state and two small massive left and right using method's Take and Skip. Code:
int[] left = state.Take(pivot).ToArray();
int[] right = state.Skip(pivot).ToArray();
pivot - split point.
There are four possible ways to get an int[]
from an IEnumerable<int>
. In order of speed:
int[]
, cast it back.Count
and CopyTo
, use that.Linq's ToArray()
will not do the first, as ToArray()
is meant to protect the souce from changes to the result of ToArray()
.
Of the rest, Linq tries to pick the fastest option. As such, you aren't going to get much of an improvement.
If casting to an array would be acceptable to you then you will be able to cover that case with:
int[] array = intEnum as int[] ?? intEnum.ToArray();
(If you know intEnum
is always an array, then you can just do the cast directly. Conversely if you know intEnum
is never an array then the above is just slower, as it always has the cost of attempting without ever having the benefit of achieving that approach).
Otherwise while you aren't going to be able to do significantly better, except in the case where you know the size of the enumerable, but it is not an ICollection<int>
so linq can't find it itself.
Edit:
A comment added to the question:
i have one big int array. By method's Take and Skip I divide it into two massive.
If you're using .NET Core then this case is already optimised for as of a commit merged into the blessed repository last month, so if you use the latest build of corefx or wait for an update then you've already got this optimised.
Otherwise you can apply the same logic as that version of Linq does:
Let's call our source array sourceArray
.
We have an IEnumerable<int>
from something like var en = sourceArray.Skip(amountSkipped).Take(amountTaken);
If amountSkipped
or amountTaken
are less than zero, then zero should be used here. If the Skip()
was left out then zero should be used for amountSkipped
. If the Take()
was left out then int.MaxValue
should be used for amountTaken
.
The size of the output array will be:
int count = Math.Min(sourceArray.Length - amountSkipped, amountTaken);
We can then create and allocate to an array:
int[] array = new int[count];
int index = 0;
foreach (int item in en)
array[index] = item;
Now array
will be filled without the need to allocate and trim, saving roughly log count
allocations. This will indeed be faster.
Again though, if you're using the lastest corefx then this is already done for you, with a further optimisation of being able to avoid the enumerator allocation.
Still, if all that was done was to Skip
and Take
then you could just drop that entirely and operate directly:
int count = Math.Min(sourceArray.Length - amountSkipped, amountTaken);
int[] array = new int[count];
Array.Copy(sourceArray, amountSkipped, array, 0, count);
No need to Skip()
and Take()
at all.
Edit again:
The question now has:
int[] left = state.Take(pivot).ToArray();
int[] right = state.Skip(pivot).ToArray();
We can make this simply:
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);
And this will indeed be a considerable improvement.
If we know pivot
is between 0 and state.Length
, then we can use the simpler:
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);