список.Взять(100).ToList () против list.GetRange(0,100)

List<AttendeeInfo> attendees = new List<AttendeeInfo>();
foreach ...
// Error: "There are too many target users in the email address array"
// for more than 100 attendees. So take the first 100 attendees only.
if(attendees.Count > 100) attendees = attendees.GetRange(0,100);
// or
if(attendees.Count > 100) attendees = attendees.Take(100).ToList();

поскольку я работаю над списком, который всегда длиннее 100, и всегда беру первые 100, самые очевидные различия (стратегия оценки, возможность пропустить, бросать на ошибки) не очень интересны.

но, возможно, вы могли бы пролить свет на то, что именно означает "создает мелкую копию диапазона элементов в исходном списке". Это звучит действительно дорого, больше, чем взять, но так ли это?

4 ответов


единственная разница в том, что List.GetRange более эффективно, чем Take(n).ToList() поскольку он уже знает размер нового списка, тогда как методы LINQ не знают его размер.

так ToList перечисляет последовательность и добавляет элементы в новый список с алгоритмом удвоения, последовательно увеличивая резервный массив. List.GetRange может создать правильный список с правильным начальным размером заранее, а затем использует Array.Copy для копирования подмножества из исходного списка в новый список [источник].


Это гораздо быстрее. Проверьте это:

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);

затраченное время:

List.GetRange(): 0.149 s

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


здесь GetRange реализация:

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;
}

и Take реализация

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;
            }
        }
    }
}

плюс ToList что просто так:

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

и List конструктора:

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);
            }
        }
    }
}

вы можете сразу отметить, сколько GetRange дешевле против Take


список.Взять(100).ToList (), было бы более уместно, если вы не знаете количество элементов в списке. Если он меньше 100, он просто возьмет те, которые доступны. более гибко использовать это.

с другой стороны, список.GetRange(0,100) предполагается, что количество элементов в списке превышает 100. Однако вы получите эту ошибку ***

смещение и длина вышли за границы массива или счет больше чем количество элементов от указателя до конца исходной коллекция

***. если количество элементов меньше указанного диапазона.

для меня, я бы сказал, что список.Взять(100).ToList () больше generic поскольку он не ограничивает использование.