Самый дешевый способ копирования IEnumerable <T>?

У меня есть IEnumerable<T>, и мне нужна его копия. Все, что реализует IEnumerable<T>, будет прекрасно. Какой самый дешевый способ его скопировать? .ToArray() может быть?

Ответ 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 = (из строки в исходной строке выбора);

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