var fillData = new List();
for (var i = 0; i < 100000; i++)
fillData.Add(i);
var stopwatch1 = new Stopwatch();
stopwatch1.Start();
var autoFill
The dissassembly from reflector for the List AddRange method has the following code
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
int count = is2.Count;
if (count > 0)
{
this.EnsureCapacity(this._size + count);
if (index < this._size)
{
Array.Copy(this._items, index, this._items, index + count, this._size - index);
}
if (this == is2)
{
Array.Copy(this._items, 0, this._items, index, index);
Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
}
else
{
T[] array = new T[count];
is2.CopyTo(array, 0);
array.CopyTo(this._items, index);
}
this._size += count;
}
}
As you can see there are some optimizations like EnsureCapacity() call and using Array.Copy().
Potentially, AddRange
can check where the value passed to it implements IList
or IList<T>
. If it does, it can find out how many values are in the range, and thus how much space it needs to allocate... whereas the foreach
loop may need to reallocate several times.
Additionally, even after allocation, List<T>
can use IList<T>.CopyTo to perform a bulk copy into the underlying array (for ranges which implement IList<T>
, of course.)
I suspect you'll find that if you try your test again but using Enumerable.Range(0, 100000)
for fillData
instead of a List<T>
, the two will take about the same time.
Because AddRange
checks size of added items and increases size of internal array only once.
i suppose this is the result of optimisation of memory allocation. for AddRange memory allocates only once, and while foreach on each iteration reallocation is done.
also may be there are some optimisations in AddRange implementation (memcpy for example)