Список <> Емкость возвращает больше элементов, чем добавлено
В List<T>
есть несколько свойств, которые, как представляется, связаны с количеством элементов в списке - Capacity
, Count
(который присутствует как свойство и метод). Это довольно запутанно, особенно по сравнению с Array
, имеющим только Length
.
Я использую List.Capacity
, но это дает неожиданный результат:
List <string> fruits = new List<string>();
fruits.Add("apple");
fruits.Add("orange");
fruits.Add("banana");
fruits.Add("cherry");
fruits.Add("mango");
Console.WriteLine("the List has {0} items in it.", fruits.Capacity);
когда я запускаю это, отображается Консоль:
the List has 4 items in it.
Я не понимаю, почему он показывает Capacity
из 8, когда я добавил только 5 элементов.
Ответы
Ответ 1
Capacity
списка представляет собой объем памяти, который в настоящее время список зарезервирован для текущих объектов и объектов, которые будут добавлены к нему. Список Count
списка состоит в том, сколько элементов фактически добавлено в список.
Ответ 2
Здесь полное объяснение свойства Capacity из MSDN:
Емкость - количество элементов, которое может хранить List<T>
до изменения размера, тогда как Подсчет - это количество элементов, которые фактически находятся в List<T>
.
Емкость всегда больше или равна Count. Если Count превышает Capacity при добавлении элементов, емкость увеличивается за счет автоматического перераспределения внутреннего массива перед копированием старых элементов и добавления новых элементов.
Емкость можно уменьшить, вызвав метод TrimExcess() или установив свойство Capacity явно. Когда значение Capacity задано явно, внутренний массив также перераспределяется для размещения указанной емкости, и все элементы копируются.
Получение значения этого свойства является операцией O (1); установка свойства - это операция O (n), где n - новая емкость.
Ответ 3
Чтобы добавить к другим ответам, поведение List по умолчанию при добавлении элементов один за другим начинается с емкости 4 и удваивает его, когда список заполняется. Это объясняет емкость 8.
Ответ 4
Чтобы понять, почему он больше, вам нужно понять, как List<T>
работает внутри. Внутри a List<T>
использует массив (so T[]
) для хранения его содержимого.
Этот массив начинается с размера 4 элемента, что эквивалентно выражению T[] array = new T[4]
. Когда вы добавляете элемент в List<T>
, он сохраняется в массиве: первый элемент в array[0]
, второй в array[1]
и т.д. Однако пятый элемент не может вписаться в этот массив, поскольку это всего лишь четыре элемента. И поскольку длина массива не может быть изменена после его создания, единственная опция - взять содержимое массива и перенести его в новый массив, который достаточно велик, чтобы удерживать этот пятый элемент. Реализация List<T>
позволяет удвоить размер буфера массива каждый раз, когда он заканчивается, поэтому, чтобы соответствовать пятому элементу, он удваивает емкость массива до 8. Затем 16 и т.д.
Существует, вероятно, хорошая математическая поддержка того, почему он выбирает удвоение, это, вероятно, хороший компромисс между дорогостоящими операциями копирования (не нужно слишком часто выделять новый буфер) и потерянным пространством. По удвоению, потери памяти никогда не превышают 50%, и количество раз, которое нужно выделить новому массиву, уменьшается логарифмически, я считаю.
Ответ 5
Емкость не совпадает с количеством элементов списка. Хорошо реализованные контейнеры списка на всех языках по умолчанию выделяют больше памяти, чем нужно, чтобы сохранить текущее количество записей. Это связано с тем, что более эффективно выделять большую часть памяти в определенное время, чем выделять память для еще одного элемента каждый раз, когда вы добавляете его.