При использовании ToList()
, есть влияние на производительность , что нужно учитывать?
Я писал запрос для извлечения файлов из каталога, который представляет собой запрос:
string[] imageArray = Directory.GetFiles(directory);
Однако, поскольку мне нравится работать List<>
, я решил добавить ...
List<string> imageList = Directory.GetFiles(directory).ToList();
Итак, есть ли какое-то влияние на производительность, которое следует учитывать при принятии решения о таком преобразовании - или его следует учитывать только при работе с большим количеством файлов? Это незначительное преобразование?
c#
arrays
performance
list
Коди
источник
источник
List<T>
в пользу a,T[]
если это сделает код более логичным / читаемым / поддерживаемым (если, конечно, преобразование не вызывает заметных проблем с производительностью, и в этом случае я бы повторно посети это я думаю).Add
илиRemove
, я бы оставил это какIEnumerable<T>
(или даже лучшеvar
)EnumerateFiles
вместоGetFiles
, чтобы был создан только один массив.GetFiles(directory)
, поскольку он реализован в .NET в настоящее время, в значительной степени делаетnew List<string>(EnumerateFiles(directory)).ToArray()
. Таким образомGetFiles(directory).ToList()
создается список, из него создается массив, а затем снова создается список. Как говорит 2kay, вам лучше делать этоEnumerateFiles(directory).ToList()
здесь.Ответы:
IEnumerable.ToList()
Да,
IEnumerable<T>.ToList()
влияет на производительность, это операция O (n), хотя она, вероятно, потребует внимания только в критических для производительности операциях.В
ToList()
операции будет использоватьсяList(IEnumerable<T> collection)
конструктор. Этот конструктор должен делать копию массива (в более общем смыслеIEnumerable<T>
), в противном случае будущие модификации исходного массива также изменятся в источнике,T[]
что в целом нежелательно.Я хотел бы повторить, что это будет иметь значение только для огромного списка, копирование фрагментов памяти - довольно быстрая операция.
Подсказка,
As
vsTo
Вы заметите, что в LINQ есть несколько методов, которые начинаются с
As
(например,AsEnumerable()
) иTo
(например,ToList()
). Методы, которые начинаются с,To
требуют преобразования, как указано выше (т. Е. Могут повлиять на производительность), а методы, которые начинаются сAs
, не требуют и просто требуют некоторого приведения или простой операции.Дополнительная информация о
List<T>
Вот еще немного подробностей о том, как
List<T>
работает, если вам интересно :)A
List<T>
также использует конструкцию, называемую динамическим массивом, размер которого необходимо изменять по требованию, это событие изменения размера копирует содержимое старого массива в новый массив. Таким образом, он начинается с малого и при необходимости увеличивается в размерах .Это разница между
Capacity
иCount
атрибутами наList<T>
.Capacity
относится к размеру скрытого массива,Count
это количество элементов,List<T>
которое всегда есть<= Capacity
. Поэтому, когда элемент добавляется в список, увеличивая егоCapacity
, размерList<T>
удваивается, и массив копируется.источник
List(IEnumerable<T> collection)
конструктор проверяет, есть ли параметр коллекции,ICollection<T>
а затем сразу же создает новый внутренний массив с требуемым размером. Если набор параметров отсутствуетICollection<T>
, конструктор выполняет итерацию по нему и вызываетAdd
каждый элемент.Да, конечно. Теоретически даже
i++
влияет на производительность, это замедляет программу, может быть, на несколько тактов.Что
.ToList
делать?Когда вы вызываете
.ToList
, код вызываетEnumerable.ToList()
метод расширения, которыйreturn new List<TSource>(source)
. В соответствующем конструкторе, в худшем случае, он проходит через контейнер элементов и добавляет их один за другим в новый контейнер. Так что его поведение мало влияет на производительность. Невозможно быть узким местом в производительности вашего приложения.Что не так с кодом в вопросе
Directory.GetFiles
проходит через папку и немедленно возвращает имена всех файлов в память, есть потенциальный риск, что строка [] будет стоить много памяти, замедляя все.Что делать тогда
Это зависит. Если вы (как и ваша бизнес-логика) гарантируете, что количество файлов в папке всегда невелико, код приемлем. Но все же предлагается использовать ленивую версию:
Directory.EnumerateFiles
на C # 4. Это больше похоже на запрос, который не будет выполнен немедленно, вы можете добавить к нему дополнительный запрос, например:Directory.EnumerateFiles(myPath).Any(s => s.Contains("myfile"))
который прекратит поиск пути, как только будет найден файл, имя которого содержит «myfile». Тогда это, очевидно, имеет лучшую производительность
.GetFiles
.источник
Да, есть. Использование метода расширения
Enumerable.ToList()
создаст новыйList<T>
объект изIEnumerable<T>
исходной коллекции, что, конечно же, повлияет на производительность.Однако понимание
List<T>
может помочь вам определить, является ли влияние на производительность значительным.List<T>
использует array (T[]
) для хранения элементов списка.List<T>
После выделения массивы не могут быть расширены, поэтому для хранения элементов списка будет использоваться массив слишком большого размера. КогдаList<T>
размер превышает размер базового массива, необходимо выделить новый массив, а содержимое старого массива необходимо скопировать в новый более крупный массив, прежде чем список сможет вырасти.Когда создается новый
List<T>
объектIEnumerable<T>
, возможны два случая:Исходная коллекция реализует
ICollection<T>
: ThenICollection<T>.Count
используется для получения точного размера исходной коллекции, и соответствующий резервный массив выделяется до того, как все элементы исходной коллекции будут скопированы в резервный массив с использованиемICollection<T>.CopyTo()
. Эта операция довольно эффективна и, вероятно, будет отображаться в какой-нибудь инструкции ЦП для копирования блоков памяти. Однако с точки зрения производительности для нового массива требуется память, а для копирования всех элементов требуются циклы ЦП.В противном случае размер исходной коллекции неизвестен, и перечислитель
IEnumerable<T>
используется для добавления каждого исходного элемента по одному к новомуList<T>
. Первоначально резервный массив пуст, и создается массив размером 4. Затем, когда этот массив слишком мал, его размер удваивается, поэтому резервный массив растет как этот 4, 8, 16, 32 и т. Д. Каждый раз, когда резервный массив растет, его необходимо перераспределить, и все элементы, сохраненные до сих пор, должны быть скопированы. Эта операция намного дороже по сравнению с первым случаем, когда массив правильного размера может быть создан сразу.Кроме того, если ваша исходная коллекция содержит, скажем, 33 элемента, список будет использовать массив из 64 элементов, тратя немного памяти.
В вашем случае исходная коллекция представляет собой массив, который реализует,
ICollection<T>
поэтому влияние на производительность не является тем, о чем вы должны беспокоиться, если ваш исходный массив не очень велик. ВызовToList()
просто скопирует исходный массив и обернет его вList<T>
объект. Даже производительность второго случая не является поводом для беспокойства для небольших коллекций.источник
Проблема с вашим точным сценарием заключается в том, что в первую очередь ваша реальная озабоченность по поводу производительности будет связана со скоростью жесткого диска и эффективностью кеша диска.
С этой точки зрения, влияние, безусловно, незначительно до такой степени, что НЕТ необходимости его рассматривать.
НО ТОЛЬКО в том случае, если вам действительно нужны функции
List<>
структуры, которые могут сделать вас более продуктивным, или ваш алгоритм более дружелюбным, или какое-то другое преимущество. В противном случае вы просто намеренно добавляете незначительное снижение производительности без всякой причины. В таком случае, естественно, делать этого не стоит! :)источник
ToList()
создает новый список и помещает в него элементы, что означает, что с действием связаны затратыToList()
. В случае небольшой коллекции это не будет очень заметной стоимостью, но наличие огромной коллекции может привести к снижению производительности в случае использования ToList.Как правило, вы не должны использовать ToList (), если ваша работа не может быть выполнена без преобразования коллекции в List. Например, если вы просто хотите перебрать коллекцию, вам не нужно выполнять ToList
Если вы выполняете запросы к источнику данных, например, к базе данных с использованием LINQ to SQL, тогда стоимость выполнения ToList намного больше, потому что когда вы используете ToList с LINQ to SQL вместо выполнения отложенного выполнения, то есть загружаете элементы при необходимости (что может быть полезно во многих сценариях) он мгновенно загружает элементы из базы данных в память
источник
Это будет столь же (не) эффективно, как выполнение:
var list = new List<T>(items);
Если вы дизассемблируете исходный код конструктора, который принимает
IEnumerable<T>
, вы увидите, что он будет делать несколько вещей:Вызов
collection.Count
, поэтому, еслиcollection
естьIEnumerable<T>
, он принудительно выполнит. Еслиcollection
это массив, список и т.д., то должно бытьO(1)
.Если
collection
реализуетICollection<T>
, он сохранит элементы во внутреннем массиве с помощьюICollection<T>.CopyTo
метода. Так и должно бытьO(n)
, учитываяn
длину коллекции.Если
collection
не реализованICollection<T>
, он будет перебирать элементы коллекции и добавлять их во внутренний список.Итак, да, он будет потреблять больше памяти, так как он должен создать новый список, и в худшем случае так и будет
O(n)
, поскольку он будет перебирать,collection
чтобы сделать копию каждого элемента.источник
0(n)
гдеn
- общая сумма байтов, которые занимают строки в исходной коллекции, а не количество элементов (точнее, n = байты / размер слова)bool
,int
И т.д.)? На самом деле вам не нужно делать копию каждой строки в коллекции. Вы просто добавляете их в новый список.Учитывая производительность получения списка файлов,
ToList()
можно пренебречь. Но не совсем для других сценариев. Это действительно зависит от того, где вы его используете.При вызове массива, списка или другой коллекции вы создаете копию коллекции как
List<T>
. Производительность здесь зависит от размера списка. Вы должны делать это, когда действительно необходимо.В вашем примере вы вызываете его в массиве. Он выполняет итерацию по массиву и добавляет элементы один за другим во вновь созданный список. Таким образом, влияние на производительность зависит от количества файлов.
При вызове на условиях
IEnumerable<T>
, вы материализовать вIEnumerable<T>
(обычно запрос).источник
ToList создаст новый список и скопирует элементы из исходного источника во вновь созданный список, поэтому единственное, что нужно сделать, это скопировать элементы из исходного источника и зависит от размера источника.
источник