Коллекции: вопросы с ответами

73 разобранных вопросов по теме «Коллекции». Каждый — с правильным ответом и пояснением.

  • В чём разница: ArrayList vs LinkedList?

    ArrayList: O(1) доступ по индексу, O(n) вставка в середину, дружелюбно к CPU-кэшу (непрерывная память). LinkedList: O(1) вставка в начало/конец (если есть ссылка), O(n) доступ по индексу, так как нужно последовательно идти по узлам двусвязного списка.

  • ArrayList vs LinkedList — где быстрее вставка в середину?

    У LinkedList O(1) на саму вставку, но O(n) на поиск позиции. В реальности ArrayList почти всегда быстрее из-за локальности в кэше CPU.

  • ArrayList vs LinkedList — что когда использовать?

    ArrayList: быстрый доступ по индексу O(1), быстрая итерация. LinkedList: быстрая вставка/удаление в начале O(1), но поиск O(n). На практике почти всегда выигрывает ArrayList.

  • В чём разница: checked vs unchecked исключения?

    Checked: от Exception (не RuntimeException) — компилятор требует throws/catch. Примеры: IOException, SQLException. Unchecked: от RuntimeException и Error — обрабатывать не обязательно. Примеры: NullPointerException, IllegalArgumentException.

  • В чём разница: ConcurrentHashMap: Java 7 vs Java 8?

    Java 7: массив Segment (ReentrantLock), каждый сегмент — свой HashMap. Java 8: убрали Segment, CAS + synchronized на головах бакетов (лучше параллелизм). Treeify при 8 коллизиях.

  • Что такое ConcurrentHashMap Java 8?

    CAS + synchronized на головах бакетов. Treeify при 8 коллизиях. null запрещён. computeIfAbsent атомарен.

  • CopyOnWriteArrayList: плюсы и минусы?

    Плюсы: lock-free чтение (читатели не блокируются), fail-safe итератор (работает со snapshot). Минусы: при каждой записи — копирование всего массива → дорого. Подходит: много чтений, редкие записи (listeners, подписчики).

  • В чём разница: Fail-fast vs fail-safe итераторы?

    Fail-fast (HashMap, ArrayList) кидают ConcurrentModificationException при изменении коллекции. Fail-safe (ConcurrentHashMap, CopyOnWriteArrayList) работают со снимком.

  • Что такое Fail-fast итератор?

    ConcurrentModificationException при изменении коллекции не через сам итератор. Реализован через сравнение modCount с expectedModCount. Не гарантирован в многопоточной среде — только best effort. Альтернатива: CopyOnWriteArrayList (fail-safe).

  • В чём разница: HashMap vs ConcurrentHashMap?

    HashMap: не потокобезопасен, допускает null-ключ. ConcurrentHashMap: Java 7 — Segment-блокировки, Java 8+ — CAS + synchronized на головах бакетов. null запрещён. computeIfAbsent — атомарная операция.

  • В чём разница: HashMap vs ConcurrentHashMap — ключевые различия?

    HashMap не потокобезопасен, допускает null-ключ/значение. ConcurrentHashMap потокобезопасен, не допускает null, в Java 7 использовал сегментную блокировку, в Java 8+ — CAS + synchronized на головах бакетов.

  • В чём разница: HashMap vs Hashtable vs LinkedHashMap vs WeakHashMap?

    HashMap: не потокобезопасен, допускает null-ключ. Hashtable: потокобезопасен (synchronized на всё), устаревший, нет null. LinkedHashMap: порядок вставки (или access order для LRU). WeakHashMap: держит ключи через слабые ссылки, записи удаляются GC, когда на ключ нет сильных ссылок.

  • В чём разница: HashMap vs TreeMap vs LinkedHashMap?

    HashMap — хэш, без порядка, O(1). TreeMap — красно-чёрное дерево, отсортирован по ключам, O(log n). LinkedHashMap — сохраняет порядок вставки или access order.

  • HashMap vs TreeMap vs LinkedHashMap — когда что?

    HashMap — быстрый доступ без порядка. TreeMap — сортировка ключей, O(log n). LinkedHashMap — сохраняет порядок вставки или access order.

  • HashMap: расчёт бакета на примере?

    Формула: (n-1) & hash(key). Если capacity=16 (n=16): 15 & hash. 15 в бинарном = 1111. hash = 2_000_000_000: 2000000000 & 15 = 0 (последние 4 бита = 0000). Результат: bucket 0. Поэтому capacity — степень двойки: побитовое И эквивалентно взятию модуля.

  • HashMap — устройство?

    Node<K,V>[]. Размер — степень двойки (default 16). Индекс: (n-1) & hash(key). Коллизии — список. Java 8+: TREEIFY_THRESHOLD=8 И capacity ≥ 64 → red-black tree.

  • HashSet — на основе чего реализован?

    На HashMap, где значение — заглушка (PRESENT). Все операции делегируются HashMap.

  • В чём разница: List.of(1,2,3) vs new ArrayList?

    List.of — immutable, add/remove → UnsupportedOperationException, null-элементы запрещены. ArrayList — обычный mutable список.

  • List.of(1,2,3) vs new ArrayList<>() — в чём разница?

    List.of возвращает неизменяемый список. add/remove бросают UnsupportedOperationException.

  • Что такое load factor и resize?

    Порог 0.75 по умолчанию. При size >= capacity * loadFactor — resize: массив вдвое + перехеширование ВСЕХ элементов (дорого!). Совет: если знаешь количество элементов — задай initialCapacity = expectedSize / 0.75 + 1.

  • Sequenced Collections (Java 21) — что это?

    Новый интерфейс с методами getFirst, getLast, addFirst, addLast, reversed. Унифицирует работу с упорядоченными коллекциями.

  • В чём разница: TreeMap vs LinkedHashMap?

    TreeMap: красно-чёрное дерево, O(log n), ключи отсортированы (Comparable/Comparator). LinkedHashMap: HashMap + двусвязный список — порядок вставки. accessOrder=true — LRU-кэш. removeEldestEntry() для ограничения размера.

  • В чём разница между List.of(1,2,3) и new ArrayList<>()?

    List.of возвращает immutable-список. Любая попытка изменить — UnsupportedOperationException. И в нём нельзя null.

  • Зачем переопределять hashCode и equals для ключа HashMap?

    HashMap использует hashCode для бакета и equals для разрешения коллизий. Если по умолчанию (Object) — каждый объект уникален, два «равных по смыслу» User'а с одинаковым id дадут разные hashCode, попадут в разные бакеты и не найдутся. Поэтому нужно переопределить оба метода согласованно, чтобы поиск по логически равному ключу работал.

  • Что такое Иерархия исключений в Java?

    Throwable → Error (системные, не ловим) и Exception. От Exception → checked и RuntimeException (unchecked).

  • Как обработать исключение в REST-контроллере?

    @ExceptionHandler в самом контроллере или глобально через @RestControllerAdvice + @ExceptionHandler.

  • Как обработать исключение глобально?

    @RestControllerAdvice + @ExceptionHandler. Внутри класса — просто @ExceptionHandler на методе контроллера.

  • Как работает put / get в HashMap?

    put: вычислили hashCode ключа → перемешали его биты и по & (n-1) определили бакет → в бакете ищем по equals существующий ключ. Если нашли — обновляем значение. Если нет — добавляем новый Node в цепочку (при переполнении бакета — в дерево). get работает симметрично: бакет по hashCode, ключ по equals.

  • Как работает resize в ArrayList?

    Новый массив в 1.5 раза больше, копирование через Arrays.copyOf.

  • Как растёт ArrayList?

    Начальная capacity = 10 (массив создаётся лениво при первом add). При переполнении создаётся новый массив размером примерно 1.5× от старого (oldCapacity + oldCapacity >> 1), элементы копируются. Метод trimToSize() ужимает массив до текущего количества элементов.

  • Как устроен ArrayList?

    Динамический массив. При нехватке места создаёт новый массив в 1.5 раза больше и копирует через Arrays.copyOf.

  • Как устроен ArrayList внутри?

    Динамический массив. При нехватке места создаёт новый массив в 1.5 раза больше и копирует данные через Arrays.copyOf.

  • Как устроен HashMap?

    Массив Node<K,V>[]. Размер — степень двойки (default 16). Индекс: (n-1) & hash(key), hash — XOR верхних 16 бит с нижними. Коллизии — связный список (chaining). Java 8+: при TREEIFY_THRESHOLD=8 И capacity ≥ 64 → red-black tree.

  • Как устроен HashMap? Что такое bucket, как разрешаются коллизии?

    Массив связных списков (Node[]). Бакет = ячейка массива. Коллизия → цепочка.

  • Как устроена HashMap внутри?

    Массив бакетов (table). По хэшу ключа определяется индекс бакета ((n-1) & hash при capacity — степени двойки). В бакете — связный список узлов Node (hash + key + value + next); при длине ≥ 8 и достаточной capacity список превращается в красно-чёрное дерево.

  • Какая сложность операций в HashMap?

    Среднее: get / put / containsKey — O(1). Худший случай при плохих хэшах: O(log n) при treeified бакете, O(n) если treeify ещё не сработал.

  • Какая сложность у HashMap.get?

    O(1) в среднем. O(log n) худший случай при treeified бакете (с Java 8). O(n) при катастрофически плохом hashCode.

  • Какие основные интерфейсы в Collection Framework?

    Collection (List, Set, Queue), Map (отдельно). List — упорядоченный с дубликатами. Set — без дубликатов. Map — пары ключ-значение.

  • Какие реализации Set?

    HashSet — на основе HashMap (значение в виде специальной заглушки). Не сохраняет порядок. LinkedHashSet — HashSet + порядок вставки. TreeSet — на основе TreeMap (красно-чёрное дерево), элементы отсортированы, операции O(log n).

  • Какое исключение лучше — выбросить своё или использовать стандартное?

    Если ситуация уникальна для домена — своё (наследник RuntimeException обычно). Если стандартное (например, IllegalArgumentException) подходит — лучше его.

  • В чём разница: Коллекции: ArrayList vs LinkedList vs HashMap?

    ArrayList: динамический массив, O(1) доступ, O(n) вставка в середину. LinkedList: двусвязный список, O(1) вставка/удаление у известного узла, O(n) доступ. HashMap: бакеты + связный список (метод цепочек) → дерево при ≥8 коллизиях.

  • Можно ли byte[] как ключ HashMap?

    Технически можно, но hashCode у массива — по адресу (identityHashCode), а equals — по ссылке. Два одинаковых по содержимому массива дадут разные хэши. Нужно обернуть в ByteBuffer или свой класс с переопределёнными equals/hashCode.

  • Можно ли null в HashMap?

    HashMap — да, ОДИН null-ключ (лежит в table[0]), null-значений сколько угодно. TreeMap — null-ключ нельзя (бросит NPE при сравнении). Hashtable — null нельзя ни в ключе, ни в значении (NPE).

  • Можно ли null-ключ в HashMap?

    Да, один null-ключ, кладётся в bucket 0. В ConcurrentHashMap нельзя ни ключ, ни значение.

  • Можно ли в HashMap положить null-ключ или null-значение?

    В HashMap — да (один null-ключ хранится в bucket 0). В ConcurrentHashMap — нельзя ни ключ, ни значение.

  • Можно ли в одном catch ловить несколько типов исключений?

    Да, multi-catch с Java 7: catch (IOException | SQLException e). Переменная при этом effectively final.

  • Можно ли изменить первичный ключ?

    Технически — да, через ALTER. Но фактически — нельзя, потому что: (1) на нём FK из других таблиц. (2) Индексы строятся по PK. (3) В кластерных индексах данные физически отсортированы по PK. Операция дорогая и опасная — требует перестройки индексов и обновления связей.

  • Можно ли использовать как ключ изменяемый объект (например, массив)?

    Технически можно, но опасно: если изменить объект после добавления — его hashCode может измениться, и найти элемент станет невозможно.

  • На какие исключения откатывается транзакция по умолчанию?

    На RuntimeException и Error. Checked-исключения НЕ откатывают по умолчанию — нужно rollbackFor = Exception.class.

  • Что такое Назови несколько unchecked исключений?

    NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException, ClassCastException, NumberFormatException.

  • Что такое Обработка исключений?

    @RestControllerAdvice + @ExceptionHandler. ErrorDto с code, message, timestamp. HTTP-статусы: 400 (валидация), 404 (не найдено), 409 (бизнес-конфликт), 500 (непредвиденные). MethodArgumentNotValidException для ошибок @Valid отдаёт 400.

  • Что такое Основные интерфейсы Collection Framework?

    Collection (List, Set, Queue) и Map (отдельно, НЕ наследует Collection). List — упорядоченный с дубликатами. Set — без дубликатов. Queue — очередь. Map — пары ключ-значение. Реализации: ArrayList, LinkedList, HashSet, HashMap.

  • Почему ArrayList на практике быстрее LinkedList?

    Cache locality. ArrayList хранит элементы в непрерывном куске памяти — процессор подгружает целые блоки в L1/L2 кэш. У LinkedList узлы разбросаны по куче — каждый next() это cache miss, что и замедляет обход на практике.

  • Почему Map отдельно от Collection?

    Collection хранит элементы — единичные значения. Map хранит ПАРЫ ключ-значение, у неё другой API: put(key, value), get(key), entrySet(). Хотя по сути обе — структуры данных.

  • Почему ключ HashMap должен быть иммутабельным?

    Если изменить поле, участвующее в hashCode — объект окажется в «неправильном» bucket. contains() вернёт false. Объект потерян — ни найти, ни удалить. Утечка памяти. Решение: immutable-ключи (String, Integer, record).

  • Что такое Разница между checked и unchecked исключениями?

    Checked наследуются от Exception (но не RuntimeException) — компилятор требует обработки или throws. Unchecked — от RuntimeException и Error — обрабатывать необязательно.

  • Что такое Разница между map и flatMap?

    map: T → R. flatMap: T → Stream с уплощением результата.

  • Что такое Расскажи иерархию исключений?

    Throwable — корень. Делится на Error (OOM, StackOverflow — не ловить) и Exception. Exception → checked (IOException, SQLException — обязаны быть в throws) и RuntimeException → unchecked (NullPointerException и потомки, которые не требуют объявления в throws).

  • Что такое Расскажи про реализации Map?

    HashMap — основная, не потокобезопасна, без порядка, null-ключ можно. LinkedHashMap — порядок вставки или access-order (последнее нужно для LRU-кэша). TreeMap — отсортирована по ключу, реализует NavigableMap (firstKey, floorKey и т.д.), операции за O(log n).

  • Расскажите про контракт equals/hashCode. Что случится с HashMap при константном hashCode?

    Если a.equals(b), то hashCode одинаков. Обратное необязательно. При константном hashCode — все в одном bucket: Java 7 — список O(n), Java 8+ при 8 коллизиях — red-black tree O(log n). Деградация производительности поиска с O(1) до O(n) или O(log n).

  • Что такое Сложность операций HashMap?

    put, get, remove — O(1) в среднем. В худшем случае O(log n) благодаря дереву (Java 8+).

  • Сложность операций HashMap: put, get, remove?

    Амортизированная O(1), в худшем случае O(log n) (Java 8+) или O(n) (Java 7).

  • Что такое Состояния потока?

    NEW (создан), RUNNABLE (готов или выполняется), BLOCKED (ждёт монитор), WAITING / TIMED_WAITING (ждёт сигнал/время), TERMINATED.

  • Что такое Сравни ArrayList и LinkedList по сложности?

    Доступ по индексу: ArrayList O(1), LinkedList O(n) (нужно идти от головы или хвоста). Вставка в конец: ArrayList амортизированный O(1), LinkedList O(1). Вставка в середину: ArrayList O(n) (сдвиг элементов), LinkedList O(1), только если итератор уже стоит на нужном узле (иначе поиск позиции O(n)).

  • Чем HashSet отличается от LinkedHashSet?

    HashSet — без гарантии порядка. LinkedHashSet хранит порядок добавления. Дополнительная память на двусвязный список, но итерация предсказуемая. Часто используют как «уникальные значения с сохранением порядка».

  • Чем Iterator отличается от ListIterator?

    Iterator — однонаправленный обход. Методы hasNext, next, remove. Доступен для любой Collection. ListIterator — только у List. Двунаправленный (hasPrevious / previous), умеет add/set и знает индексы (nextIndex / previousIndex).

  • Чем отличается List от ArrayList?

    List — интерфейс, ArrayList — реализация. Правило: писать переменные через интерфейс — List<String> list = new ArrayList<>(); — чтобы потом можно было сменить реализацию без правок остального кода.

  • Что будет, если использовать как ключ изменяемый объект и потом его изменить?

    Объект «потеряется». hashCode станет другим, и при поиске мы попадём не в тот bucket.

  • Что поменялось в HashMap с Java 8?

    При 8 элементах в бакете и размере таблицы ≥ 64 список превращается в red-black tree. Поиск становится O(log n) вместо O(n) в худшем случае.

  • Что произойдёт, если положить объект в HashSet, а потом изменить поле, участвующее в hashCode?

    Потеряешь объект — найти его через contains() будет уже нельзя. Классическая ошибка с mutable-объектами в коллекциях.

  • Что такое checked и unchecked исключения?

    Checked (наследники Exception, кроме RuntimeException) — компилятор требует обработать (catch) или объявить в throws; применяются для ожидаемых внешних ошибок (IO, БД). Unchecked (RuntimeException и его наследники, а также Error) обрабатывать не обязательно.

  • Что такое fail-fast итератор?

    Итератор, который кидает ConcurrentModificationException, если коллекция изменена не через сам итератор. Так работают итераторы у HashMap, ArrayList.

  • Что такое load factor?

    Пороговое соотношение size / capacity (по умолчанию 0.75), при превышении которого таблица расширяется. То есть когда size превышает 75% от capacity, capacity увеличивается в 2 раза и все элементы перехэшируются под новый размер.

новые гайды и свежие вопросы с собесов — первыми в Telegram Смотреть гайды Подписаться