Что такое Capacity у списка (List) в C#?

Ответ

Capacity — это свойство класса List<T>, которое определяет общее количество элементов, которое внутренний массив списка может вместить без перераспределения памяти. Фактическое количество элементов в списке определяется свойством Count.

Как это работает: При создании списка выделяется внутренний массив. Когда вы добавляете элементы (Add) и Count достигает значения Capacity, происходит автоматическое увеличение емкости (обычно в 2 раза), что влечет за собой:

  1. Выделение нового, большего массива.
  2. Копирование всех существующих элементов из старого массива в новый.
  3. Продолжение работы с новым массивом.

Пример:

// Создаем список с начальной Capacity = 4
List<int> numbers = new List<int>(4);
Console.WriteLine(numbers.Capacity); // 4

numbers.Add(1); // Count = 1, Capacity = 4
numbers.AddRange(new[] { 2, 3, 4 }); // Count = 4, Capacity = 4
numbers.Add(5); // Count = 5, Capacity увеличивается до 8
Console.WriteLine(numbers.Capacity); // 8

Зачем это нужно и best practices:

  • Производительность: Перераспределение массива — дорогая операция. Если вы заранее знаете примерное количество элементов, указание начальной Capacity в конструкторе позволяет избежать множественных переаллокаций.
  • Соотношение Capacity и Count: Capacity всегда >= Count. Присвоение значения Capacity меньшего, чем текущий Count, выбросит ArgumentOutOfRangeException.
  • Оптимизация памяти: Если после массового удаления элементов Capacity значительно превышает Count, можно вызвать метод TrimExcess(), чтобы уменьшить Capacity ближе к Count (реализация не гарантирует точного равенства).
  • Прямое управление: Свойство Capacity имеет сеттер, но его использование требует осторожности.

Ответ 18+ 🔞

Давай разберём эту штуку про Capacity в List<T>, чтобы было понятно, как муха с котлетой.

Представь, что List<int> — это такой прожорливый чемодан на колёсиках. Так вот:

  • Count — это сколько носков и трусов ты в него уже напихал.
  • Capacity — это на сколько пар носков этот чемодан вообще рассчитан, пока у него молния не начнёт трещать по швам.

Как оно живёт: Создаёшь ты пустой список — ему выделяют внутри массив-кармашек, скажем, на 4 элемента. Пока ты кидаешь туда свои циферки (Add), Count растёт, а Capacity спокойно курит в сторонке. Но в один прекрасный момент ты пытаешься запихнуть пятый элемент, а места-то уже нет, блядь!

Тогда список, недолго думая, делает вот что: берет новый чемодан (массив) в два раза больше, перекладывает все старые вещи (элементы) аккуратненько на свои места, а старый чемодан выкидывает на помойку (отдаёт сборщику мусора). И так каждый раз, когда ему становится тесно. Операция, конечно, не из быстрых — представь, как ты каждый раз перепаковываешь весь багаж.

Смотри, как это выглядит в коде:

// Делаем чемодан, который изначально рассчитан на 4 пары носков (Capacity = 4)
List<int> numbers = new List<int>(4);
Console.WriteLine(numbers.Capacity); // 4 — вот его заводская вместимость

numbers.Add(1);    // Кинул один носок. Count = 1, Capacity = 4 (ещё овердохуя места)
numbers.AddRange(new[] { 2, 3, 4 }); // Докинул ещё три. Count = 4, Capacity = 4 (чемодан полон!)
numbers.Add(5);    // А вот пытаешься запихнуть пятый... Ёпта! Места нет!
                   // *звуки перепаковки багажа*
                   // Count = 5, Capacity теперь увеличен до 8
Console.WriteLine(numbers.Capacity); // 8 — вот новый, просторный чемодан!

Зачем это всё надо и как не выстрелить себе в ногу:

  1. Производительность, ёб твою мать! Если ты заранее знаешь, что у тебя будет, ну, тысяч 100 записей, то сразу укажи это в конструкторе: new List<int>(100_000). Чемодан сразу сделают здоровенным, и он не будет каждые 5 секунд перепаковываться. Экономия — реальная.

  2. Capacity всегда >= Count. Это как аксиома. Нельзя сделать чемодан меньше, чем вещей в нём уже лежит. Попробуешь — получишь ArgumentOutOfRangeException прямо в ебальник.

  3. Жрать меньше памяти. Допустим, ты был максималистом, сделал Capacity на миллион, а потом 999990 элементов удалил. Получился полупустой чемодан размером с хоккейную коробку. Чтобы не выглядеть идиотом, можно вызвать TrimExcess(). Он попробует ужать внутренний массив поближе к реальному Count. Ключевое слово — попробует, гарантий тебе никаких.

  4. Ручное управление. У Capacity есть сеттер. Менять его можно, но осторожно, как матан в универе. Если поставишь значение меньше Count — см. пункт 2, будет больно. Если больше — просто выделится новый массив и всё переедет. Обычно в этом нет смысла, лучше сразу в конструкторе указать.

Короче, Capacity — это про то, чтобы твой список не дрочился с постоянным перевыделением памяти, когда ты этого не ожидаешь. Предусмотрительность рулит.