list.Take(100).ToList() vs. list.GetRange(0,100)

The only difference is that List.GetRange is more efficient than Take(n).ToList() since it already knows the size of the new list whereas the LINQ methods don't know it's size.

So ToList enumerates the sequence and adds the items to a new list with a doubling algorithm increasing the backing array consecutively. List.GetRange can create the proper list with the right initial size beforehand and then uses Array.Copy to copy the subset of the source list into the new list [source].


It is much more faster. Check this out:

var list = Enumerable.Range(0, 1000).ToList();

var stopwatch = new Stopwatch();

stopwatch.Start();

for(var i=0; i<1000000; i++)
{
    var c = list.GetRange(0, 100);
}

Console.WriteLine(stopwatch.Elapsed);

stopwatch.Restart();

for (var i = 0; i < 1000000; i++)
{
     var c = list.Take(100).ToList();
}

Console.WriteLine(stopwatch.Elapsed);

Elapsed time:

List.GetRange() : 0.149 s

List.Take().ToList() : 3.625 s


Here is GetRange implementation:

public List<T> GetRange(int index, int count)
{
    if (index < 0)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
    }
    if (count < 0)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
    }
    if ((this._size - index) < count)
    {
        ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
    }
    List<T> list = new List<T>(count);
    Array.Copy(this._items, index, list._items, 0, count); // Implemented natively
    list._size = count;
    return list;
}

And this is Take Implementation

public static IEnumerable<TSource> Take<TSource>(this IEnumerable<TSource> source, int count)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    return TakeIterator<TSource>(source, count);
}

private static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count)
{
    if (count > 0)
    {
        foreach (TSource iteratorVariable0 in source)
        {
            yield return iteratorVariable0;
            if (--count == 0)
            {
                break;
            }
        }
    }
}

Plus ToList that simply does:

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    return new List<TSource>(source);
}

And List constructor:

public List(IEnumerable<T> collection)
{
    if (collection == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
    }
    ICollection<T> is2 = collection as ICollection<T>;
    if (is2 != null)
    {
        int count = is2.Count;
        if (count == 0)
        {
            this._items = List<T>._emptyArray;
        }
        else
        {
            this._items = new T[count];
            is2.CopyTo(this._items, 0);
            this._size = count;
        }
    }
    else
    {
        this._size = 0;
        this._items = List<T>._emptyArray;
        using (IEnumerator<T> enumerator = collection.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                this.Add(enumerator.Current);
            }
        }
    }
}

You can note immediately how much GetRange is cheaper against Take

Tags:

C#

Linq