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

前端 未结 5 1719
臣服心动
臣服心动 2021-02-19 11:57
List attendees = new List();
foreach ...
// Error: \"There are too many target users in the email address array\"
// for more tha         


        
相关标签:
5条回答
  • 2021-02-19 12:23

    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

    0 讨论(0)
  • 2021-02-19 12:34

    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

    0 讨论(0)
  • 2021-02-19 12:36

    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].

    0 讨论(0)
  • List.Take(100).ToList(), would be more appropriate if you don't know the number of elements in the list. If it is less than 100, It would just take the ones available. it is more flexible to use this.

    On the other hand, List.GetRange(0,100) assumes that the number of elements in the list is more than 100. You would however get this error ***

    Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection

    ***. if the number of elements is less than the specified range.

    For me, I would say that List.Take(100).ToList() is more generic since it doesn't restrict usage.

    0 讨论(0)
  • 2021-02-19 12:47

    There is a minor between Take and GetRange. Take will evaluate lazily until you force it to evaulate into ToList(). This can change behavior.

    Consider the pseudo code.

    List<int> myList = new List<int>() {1,2,3,4,5,6};
    IEnumerable<int> otherList = myList.Take(3);  // {1,2,3};
    myList.RemoveRange(0,3);
    // myList = {4, 5, 6}
    // otherList = {4, 5, 6}
    

    Now change this example to following code.

    List<int> myList = new List<int>() {1,2,3,4,5,6};
    IEnumerable<int> otherList = myList.Take(3).ToList();  // {1,2,3};
    myList.RemoveRange(0,3);
    // myList = {4, 5, 6}
    // otherList = {1, 2, 3}
    

    Doing ToList() can change the behavior of Take or GetRange depending upon what operations you are using next. It is always easier to use GetRange since it will not cause any unknown bugs.

    Also GetRange is efficient.

    0 讨论(0)
提交回复
热议问题