Cheapest way to copy an IEnumerable<T>?
I've got an IEnumerable<T>
, and I need a copy of it. Anything that implements IEnumerable<T>
will do just fine. What's the cheapest way to copy it? .T开发者_Go百科oArray()
maybe?
ToArray
is not necessarily faster than ToList
. Just use ToList
.
The point is as long as you don't know the number of elements of the original sequence before enumerating, you end up with resizing an array and adding elements to it like a List<T>
does, so ToArray
will have to do the same thing a List<T>
does anyway. Besides, ToList
gives you a List<T>
and that's nicer than a raw array.
Of course, if you know the concrete type of the IEnumerable<T>
instance, there can be faster methods, but that's not germane to the point.
Side note: using an array (unless you have to) is arguably a micro-optimization and should be avoided most of the time.
Enumerable::ToArray
and Enumerable::ToList
ultimately use the same technique to receive elements from the source into an internal array buffer and, once the size of that buffer is reached, they will allocate a new buffer double the size, memcpy over and continue adding elements, repeating this process until enumeration over the source is complete. The difference in the end is that ToArray
, which uses a Buffer<T>
implementation internally, must then allocate an exactly sized Array
and copy the elements into it before returning the result. On the other hand, ToList
just needs to return the List<T>
with a potentially (likely) only partially filled array buffer inside of it.
Both implementations also have an optimization where if the source IEnumerable
is an ICollection
they will actually allocate the exact right buffer size to begin with using ICollection::Count
and then use ICollection::CopyTo
from the source to fill their buffers.
In the end you will find that they perform nearly identically in most situations, but the List<T>
is technically a "heavier" class to hang on to in the end and the ToArray
has that extra allocate + memcpy at the end (if the source isn't an ICollection
) to be able to hand back the exactly right sized array. I usually stick with ToList
myself unless I know I need to pass the result to something that requires an array like say maybe Task::WaitAll
.
I was about to suggest the possibility of using .AsParallel().ToList()
if you have TPL at your disposal, but informal testing on my dual-core laptop shows it to be 7x slower than just .ToList()
. So, stick with Mehrdad's answer.
The second-to-cheapest way is to say new List<T>(myEnumerable).ToArray()
. The cheapest way is to use either .ToArray()
(from LINQ) or, if you don't have C# 3.5, to create your own buffer and add to it while doubling its size, then trim it at the end.
精彩评论