When we do .ToList()
for an IEnumerable
, the list can potentially reallocate while scanning the IEnumerable
because it doesn't know the size upfront. If the size is known, is there a simple way to avoid the performance penalty? Something to the effect of initializing a List
with the required capacity and then copying the IEnumerable
into it? Ideally something as simple as .ToList(capacity)
(which doesn't exist).
ToList() does have a performance impact, it is an O(n) operation though it will likely only require attention in performance critical operations.
The tolist() function is used to convert a given array to an ordinary list with the same items, elements, or values.
The ToList<TSource>(IEnumerable<TSource>) method forces immediate query evaluation and returns a List<T> that contains the query results. You can append this method to your query in order to obtain a cached copy of the query results.
ToList() makes a shallow copy. The references are copied, but the new references still point to the same instances as the original references point to.
In cases when the capacity is part of IEnumerable<T>
that is also an ICollection<T>
, the library will allocate at the correct capacity.
Here is a reference implementation of List<T>(IEnumerable<T> source)
, which is invoked when you call ToList()
:
public List(IEnumerable<T> collection) {
if (collection==null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
Contract.EndContractBlock();
ICollection<T> c = collection as ICollection<T>;
if( c != null) {
int count = c.Count;
if (count == 0) {
_items = _emptyArray;
} else {
_items = new T[count];
c.CopyTo(_items, 0);
_size = count;
}
} else {
_size = 0;
_items = _emptyArray;
// This enumerable could be empty. Let Add allocate a new array, if needed.
// Note it will also go to _defaultCapacity first, not 1, then 2, etc.
using(IEnumerator<T> en = collection.GetEnumerator()) {
while(en.MoveNext()) {
Add(en.Current);
}
}
}
}
Note how the constructor behaves when collection
implements ICollection<T>
: rather than iterating the content and calling Add
for each item, it allocates the internal _items
array, and copies the content into it without reallocations.
In situations when the capacity is not embedded in class implementing IEnumerable<T>
, you can easily define one yourself, using a combination of standard methods:
public static class ToListExtension {
public static List<T> ToList<T>(this IEnumerable<T> source, int capacity)
{
var res = new List<T>(capacity);
res.AddRange(source);
return res;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With