Какие основные реализации интерфейса `List` в Java?

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

Ответ

Ключевые реализации List<E> и их характеристики:

Реализация Внутренняя структура Ключевые особенности Сценарий использования
ArrayList Динамический массив (Object[]). Быстрый доступ по индексу (get, set) — O(1). Медленные вставка/удаление в середине (требует сдвига) — O(n). Стандартный выбор, когда преобладает доступ по индексу и перебор, а изменения — в конце списка.
LinkedList Двусвязный список. Быстрая вставка/удаление в начале/середине — O(1). Медленный доступ по индексу — O(n). Также реализует Deque. Подходит для частых вставок/удалений, особенно в начале списка, или когда нужна очередь (Queue/Deque).
Vector Аналогичен ArrayList (массив). Устарел. Все методы синхронизированы (потокобезопасны), что приводит к избыточным накладным расходам в однопоточном коде. Не рекомендуется для нового кода.
CopyOnWriteArrayList Массив. Потокобезопасна. При любой модификации создается новая копия внутреннего массива. Чтение не блокируется. Идеальна для сценариев «частое чтение, редкая запись» (например, списки слушателей событий).
Stack Наследует Vector. Устарел. Реализует LIFO-стек (методы push, pop). Не рекомендуется. Для стека используйте реализацию Deque, например, ArrayDeque.

Пример выбора:

// Для частого доступа по индексу и перебора:
List<String> catalog = new ArrayList<>();

// Для частых вставок/удалений в начале или как очередь:
List<Integer> history = new LinkedList<>();

// Для потокобезопасного списка слушателей:
CopyOnWriteArrayList<Runnable> listeners = new CopyOnWriteArrayList<>();