У меня есть IEnumerable<T>
, и мне нужна его копия. Все, что реализует IEnumerable<T>
, будет прекрасно. Какой самый дешевый способ его скопировать? .ToArray()
может быть?
Самый дешевый способ копирования IEnumerable <T>?
Ответ 1
ToArray
не обязательно быстрее, чем ToList
. Просто используйте ToList
.
Точка до тех пор, пока вы не знаете количество элементов исходной последовательности перед перечислением, вы закончите с изменением размера массива и добавлением к нему элементов, как это делает List<T>
, поэтому ToArray
будет иметь сделать то же самое, что и List<T>
. Кроме того, ToList
дает вам List<T>
и это лучше, чем необработанный массив.
Конечно, если вы знаете конкретный тип экземпляра IEnumerable<T>
, могут быть более быстрые методы, но это не связано с точкой.
Боковое примечание: использование массива (если вам нужно), возможно, является микро-оптимизацией и должно быть избегать большую часть времени.
Ответ 2
Enumerable::ToArray
и Enumerable::ToList
в конечном итоге используют тот же метод для приема элементов из источника во внутренний буфер массива и, как только размер этого буфера будет достигнут, они будут выделять новый буфер с удвоенным размером, memcpy over и продолжайте добавлять элементы, повторяя этот процесс до тех пор, пока не будет завершена перепись по источнику. Разница в конце заключается в том, что ToArray
, который использует реализацию Buffer<T>
внутри себя, должен затем выделить точно размер Array
и скопировать элементы в него перед возвратом результата. С другой стороны, ToList
просто нужно вернуть List<T>
с потенциально (вероятным) только частично заполненным буфером массива внутри него.
Обе реализации также имеют оптимизацию, где, если исходный IEnumerable
является ICollection
, они будут фактически распределять точный правый размер буфера, чтобы начать с использования ICollection::Count
, а затем использовать ICollection::CopyTo
из источника для заполнения своих буферов.
В итоге вы обнаружите, что в большинстве случаев они выполняются почти одинаково, но List<T>
является технически "более тяжелым" классом, в конце которого нужно висеть, а ToArray
имеет дополнительное выделение + memcpy на end (если источник не является ICollection
), чтобы иметь возможность передать массив точно определенного размера. Я обычно придерживаюсь ToList
сам, если не знаю, что мне нужно передать результат тому, что требует массив, например say Task::WaitAll
.
Ответ 3
Я собирался предложить возможность использования .AsParallel().ToList()
, если у вас есть TPL в вашем распоряжении, но неофициальное тестирование на моем двухъядерном ноутбуке показывает, что оно будет на 7 раз медленнее, чем просто .ToList()
. Итак, придерживайтесь Mehrdad's .
Ответ 4
Самый дешевый способ - сказать new List<T>(myEnumerable).ToArray()
. Самый дешевый способ - использовать либо .ToArray()
(из LINQ), либо, если у вас нет С# 3.5, для создания собственного буфера и добавления к нему при удвоении его размера, а затем обрезать его в конце.
Ответ 5
Я знаю, что это довольно старый...
Почему бы вам не сделать что-то подобное...
IEnumerable < Т > original = {... вставить код здесь, чтобы заполнить...};
IEnumerable < Т > copy = (из строки в исходной строке выбора);
Я использовал этот метод раньше, чтобы отложить загрузку копии и ее элементов до тех пор, пока я им не понадоблюсь. Кроме того, я не изменяю базовый тип данных "оригинал", перебрасывая его в тип списка.