Получить доступ

Почему добавление в конец ArrayList считается амортизированно O(1), хотя иногда требует копирования всего массива?

MiddleКоллекции
Ещё спрашивают так:
  • Как растёт ArrayList?
  • Почему add в ArrayList считают амортизированной O(1), если при переполнении происходит копирование всего массива?
Коротко

Ёмкость растёт в 1,5 раза: копирования всё реже, сумма копирований на n вставок — геометрическая прогрессия, то есть O(n). На одну вставку — константа в среднем, хотя отдельная вставка бывает O(n).

  • Ёмкости: 10, 15, 22, 33, 49, 73…
  • Рост на константу дал бы O(n²) в сумме
  • Известный размер — задать ёмкость в конструкторе

А ещё «Амортизированно» — в среднем по последовательности операций, без вероятностей

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Попробуйте сами

Сколько раз ArrayList копирует массив

Подробно

Ключ в мультипликативном росте: в HotSpot новая ёмкость считается как старая плюс половина старой. Из-за этого расширения происходят всё реже, а сумма всех копирований образует убывающую геометрическую прогрессию и остаётся линейной от n. Если бы массив рос на фиксированную величину, суммарная стоимость была бы квадратичной. Важно понимать, что амортизированная оценка — про среднее по серии операций: отдельное добавление, попавшее на расширение, всё равно стоит O(n), и для чувствительных к задержке участков ёмкость задают заранее через конструктор.

Что хотят услышать

  • Мультипликативный рост и редкость копирований.
  • Расчёт суммарной работы.
  • Отличие от роста на константу.

Частые ошибки

  • Путать амортизированную оценку с гарантией на каждую операцию.
  • Считать, что ArrayList растёт вдвое — в HotSpot это полтора раза.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓Ёмкость растёт мультипликативно, поэтому суммарная стоимость копирований на n добавлений линейна, и в среднем на элемент приходится константа
  • ✗Копирование выполняется в фоновом потоке и не влияет на время добавления
  • ✗Массив расширяется на один элемент, поэтому копирование дёшево
  • ✗JVM оптимизирует копирование до константного времени

Что спросят дальше

  • Когда стоит задавать начальную ёмкость?
  • Уменьшается ли ArrayList автоматически после удалений?
  • Почему вставка в середину остаётся O(n) даже при запасе ёмкости?
  • Что делает trimToSize и когда он нужен?

Разбор темы

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем