Какую структуру данных лучше использовать для частого удаления первого элемента в Java
Что проверяют
Знаете ли вы, как коллекции устроены внутри, и не остановились ли на ответе из старых учебников «берите LinkedList». Следом обычно спрашивают, почему ArrayList.remove(0) медленный и что выбрать, если доставать нужно по приоритету.
Как отвечать
Лучший ответ сегодня: ArrayDeque. Это кольцевой буфер на массиве с индексами головы и хвоста. pollFirst() и removeFirst() просто сдвигают голову, это O(1) без копирования. Документация JDK прямо пишет, что в роли очереди ArrayDeque скорее всего быстрее LinkedList.
Сравнение, которое стоит проговорить:
ArrayList.remove(0): O(n), после удаления весь хвост сдвигается черезSystem.arraycopy. На большом списке и частых удалениях это квадратичная работа;LinkedList.removeFirst(): тоже O(1), но каждый элемент живёт в отдельном узле со ссылками вперёд и назад. Больше памяти, хуже локальность, больше работы сборщику мусора;ArrayDeque: O(1), элементы лежат в одном массиве, лишних объектов нет. Ограничение:nullхранить нельзя, получитеNullPointerException;PriorityQueue: если «первый» значит минимальный по приоритету, а не самый старый.poll()стоит O(log n);- многопоточность:
ArrayBlockingQueue,LinkedBlockingQueueилиConcurrentLinkedDeque, в зависимости от того, нужны ли блокировка и ограничение размера.
Хорошо дополнить разницей методов: removeFirst() на пустой очереди бросает NoSuchElementException, а pollFirst() возвращает null. Для цикла обработки очереди удобнее второй. Переменную объявляйте типом Deque или Queue, тогда реализацию легко заменить.
Где спотыкаются
Отвечают LinkedList и на этом останавливаются. Формально верно, но интервьюер ждёт ArrayDeque и объяснения, почему он выигрывает на реальном железе.
Видят в Java 21 метод removeFirst() у List (он пришёл вместе с SequencedCollection) и решают, что у ArrayList удаление первого стало дешёвым. Метод новый, а стоимость прежняя: O(n) со сдвигом.
Не уточняют, что значит «первый». Если задача про самый срочный элемент, очередь по порядку вставки даст неверный результат, нужна PriorityQueue.