Какова начальная ёмкость (capacity) ArrayList по умолчанию в Java и как она изменяется?

Ответ

Начальная ёмкость (capacity) внутреннего массива ArrayList при создании через конструктор без параметров равна 0, а не 10, начиная с Java 8. Массив создаётся лениво, при первом добавлении элемента.

Механизм работы:

  1. Конструктор ArrayList(): Инициализирует внутренний массив elementData пустой константой DEFAULTCAPACITY_EMPTY_ELEMENTDATA.
    // Java 8+
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // {} (пустой массив)
    }
  2. Первое добавление (add(E e)): При первом вызове add() проверяется, является ли массив пустой константой. Если да, то ёмкость увеличивается до значения DEFAULT_CAPACITY, которое равно 10.
    private static final int DEFAULT_CAPACITY = 10;
  3. Последующее увеличение: Когда внутренний массив заполняется, его ёмкость увеличивается по формуле newCapacity = oldCapacity + (oldCapacity >> 1), что примерно равно увеличению в 1.5 раза.

Конструкторы для явного указания ёмкости:

// Создаёт ArrayList с начальной ёмкостью 20
ArrayList<String> listWithCapacity = new ArrayList<>(20);

// Создаёт список, инициализированный другой коллекцией.
// Начальная ёмкость равна размеру переданной коллекции.
ArrayList<String> listFromCollection = new ArrayList<>(someOtherList);

Практические рекомендации:

  • Избегайте дефолтного конструктора, если заранее известен примерный размер коллекции. Указание начальной ёмкости предотвращает многократные дорогостоящие операции копирования массива при росте.
  • size() vs capacity(): size() возвращает количество элементов в списке, а capacity() — длину внутреннего массива (этот метод private, но его логику можно понять).

Ответ 18+ 🔞

А, слушай, смотри, тут народ часто путается, как там этот ArrayList внутри устроен, особенно про начальный размер. Ну, типа, все помнят, что там десятка, да? А вот хуй там, с восьмой Java всё поменялось, блядь!

Вот представь: ты создаёшь список через new ArrayList<>(). И думаешь: "Ну всё, там внутри массив на 10 ячеек уже лежит, готовый". А нихуя подобного, ёпта! На самом деле, внутри сейчас ноль, полный ноль! Массив-то создаётся лениво, как последняя мартышка, только когда ты первый элемент сунешь. До этого там пустая заглушка торчит.

Как это работает, блядь:

  1. Создал список — получил пшик. Конструктор без параметров просто тычет ссылку на пустую константу. Никакого массива ещё нет, ядрёна вошь!
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // По сути, просто {}
    }
  2. Первый add() — и понеслась. Ты добавляешь первый элемент, и код такой: "О, так тут же пустышка! Ладно, создаём нормальный массив". И создаёт его размером DEFAULT_CAPACITY, а это как раз та самая десятка.
  3. Дальше — по накатанной. Массив заполнился? Щас мы его расширим! Берут старый размер, сдвигают вправо (это деление на два, но типа быстро), и прибавляют. Получается примерно в полтора раза больше. Старый массив — нахуй, новый — красава, всё туда копируется.

А если ты не лох и размер примерно знаешь? То есть конструкторы, где можно не ждать милости от JVM:

// Сразу говоришь: дай мне массив на 20, чтобы не дергался потом
ArrayList<String> listWithCapacity = new ArrayList<>(20);

// Или скопировал из другой коллекции — размер подстроится
ArrayList<String> listFromCollection = new ArrayList<>(someOtherList);

Вывод, чтобы не быть мудаком:

  • Не используй дефолтный конструктор просто так, если знаешь, сколько примерно элементов будет. Сказал размер заранее — избежал кучи лишних копирований массива, когда он будет расти. Это ж операция не из лёгких, блядь!
  • И запомни разницу, а то опозоришься: size() — это сколько элементов ты уже засунул, а ёмкость (capacity) — это сколько всего можно засунуть прямо сейчас, пока массив снова не порвётся. Только вот capacity — штука приватная, наружу не торчит, но логику-то понимать надо, а то как жить?