Эффективен ли ArrayList для частого добавления элементов в конец?

«Эффективен ли ArrayList для частого добавления элементов в конец?» — вопрос из категории Алгоритмы и структуры данных, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, ArrayList в Java очень эффективен для добавления элементов в конец (амортизированная сложность O(1)).

Как это работает: ArrayList хранит элементы во внутреннем массиве. Добавление в конец (add(E element)) обычно выполняется за константное время, так как просто записывает элемент в следующую свободную ячейку.

Нюанс — расширение массива: Когда внутренний массив заполняется, происходит операция grow(): создается новый массив большего размера (обычно в 1.5 раза), и все элементы копируются в него. Эта операция имеет сложность O(n), но происходит относительно редко, что дает амортизированную O(1).

Оптимизация: Если известен примерный конечный размер коллекции, инициализируйте ArrayList с указанием initialCapacity. Это предотвратит многократные дорогостоящие копирования.

// Оптимально для добавления 100_000 элементов
List<Integer> largeList = new ArrayList<>(100_000);
for (int i = 0; i < 100_000; i++) {
    largeList.add(i); // Ни одного внутреннего копирования массива.
}

Сравнение с LinkedList: Для операции add в конец:

  • ArrayList: Быстрее на практике из-за лучшей локальности данных в памяти (элементы лежат рядом) и меньших накладных расходов на один элемент (не нужны объекты-узлы).
  • LinkedList: Теоретически тоже O(1), но требует создания нового узла и обновления ссылок, что работает медленнее в реальности.

Вывод: ArrayList — лучший выбор для сценариев с частым добавлением в конец, особенно при известном или предполагаемом размере.