Собес в Яндекс Путешествия (Яндекс Вертикали) · Java Middle¶
Гайд для подготовки к Java Middle interview в Яндекс Путешествия: Java/Kotlin, Spring Boot, PostgreSQL, YDB, gRPC, алгоритмы и System Design. Подходит для подготовки к live coding, backend-секции и архитектурным вопросам.
Темы: Java · Kotlin · Spring Boot · PostgreSQL · YDB · gRPC · Logbroker · Kubernetes
← Ко всем гайдам · Канал JavaJub в Telegram
1. Яндекс Путешествия и формат собеса¶
Яндекс Путешествия — часть бизнес-юнита Яндекс Вертикали (вместе с Авто.ру, Недвижимостью, Арендой). Это единственное крупное JVM-направление Яндекса наряду с Маркетом — можно прийти с обычным Java-бэкграундом, не переучиваясь на C++, Go или Scala. Команда ~40+ бэкенд-разработчиков. Продукты: отели, авиа, ж/д, автобусы, B2B-командировки, экстранет для отельеров, биллинговая платформа. Нагрузка — десятки тысяч RPS, миллионы DAU.
Как устроен процесс¶
Яндекс нанимает через общий поток «бэкенд в Путешествия». Уровень (Middle / Middle+ / Senior) определяется по итогам секций, а не указывается в вакансии. Секции оцениваются независимо — интервьюеры не обсуждают результаты до финальной калибровки.
| Этап | Формат | Длительность |
|---|---|---|
| HR-скрининг | Zoom, мотивация, ЗП | 30–60 мин |
| Техническое предварительное | Yandex.Code (без компиляции) | 1 час |
| Advanced Code (с 2025) | IDE кандидата + автотесты + | 1 час |
интернет
| Алгоритмическая секция | Yandex.Code, 2–3 задачи | 1 час |
|---|---|---|
| Java Core + многопоточность | Yandex.Code, теория + код | 1–1.5 часа |
| System Design | Miro + Zoom | 1 час (опц. для Middle) |
| Секция «про опыт» (STAR) | Разбор проектов | 1 час |
| Финалы с командами | 2–5 команд по 1 часу | 2–5 часов |
ФИШКА. Ключевая особенность Можно провалить 1–2 секции и всё равно получить оффер — секции оцениваются независимо. В сомнительных случаях просто назначают дополнительную секцию. После отказа можно перезайти через 6–12 месяцев.
ВНИМАНИЕ · Про алгоритмы Алгоритмическая секция — главный фильтр. Уровень сравним с Google. НО: Яндекс официально заявляет, что НЕ дают динамическое программирование, Дейкстру, KMP. Фокус — хэш-таблицы, два указателя, sliding window, BFS/DFS.
2. Стек по вакансии¶
Стек определён по вакансии «Разработчик на Java в Путешествия» (yandex.ru/jobs) и карточке на career.habr.com. Путешествия отличаются от остальных Вертикалей: Авто.ру пишет на Scala + ZIO, а Путешествия — на Java + Kotlin + Spring.
Основной стек¶
-
Java + Kotlin — основные языки (Go и C++ для отдельных компонентов)
-
Spring / Spring Boot — основной фреймворк
-
Hibernate + JOOQ — ORM и типизированный SQL
-
PostgreSQL — OLTP-база
-
YDB — распределённая база Яндекса (высокая нагрузка, отказоустойчивость)
-
gRPC — внутренний RPC, HTTP/REST — наружу
-
Logbroker — внутренняя шина данных (поверх YDB Topics, API-совместима с Kafka)
-
YT / YTsaurus — аналитика, MapReduce, холодное хранилище
-
Arcadia — монорепозиторий Яндекса
-
Nanny / Yandex Deploy — деплой и оркестрация
Что важно знать¶
-
Требование: 5+ лет коммерческой разработки на Java или Kotlin
-
Внутренние инструменты (YDB, Logbroker, Arcadia) осваиваются на месте — не нужно знать до собеса
-
Новичок проводит двухнедельный буткемп в каждой команде перед выбором
-
Опционально: MultiTrack-буткемп — 8 недель работы в 3 командах с полной зарплатой
СОВЕТ. Для Java-разработчика Путешествия — наиболее «дружественное» подразделение Яндекса для классического Java-разработчика: привычный стек Spring/Hibernate/PostgreSQL + Kotlin. Внутренние технологии (YDB, Logbroker, Arcadia) изучаются уже на месте.
3. Java Core — что точно спросят¶
По отзывам кандидатов (Medium, Habr 968968, Habr 854956): equals/hashCode + HashMap — абсолютные чемпионы. Далее — Stream API, generics, String Pool, ссылочные типы.
Базовые вопросы¶
-
JDK, JRE, JVM? JVM — виртуальная машина (исполняет байт-код). JRE = JVM + стандартные библиотеки. JDK = JRE + инструменты (javac, jdb, jar). С Java 9 JRE отдельно не поставляется.
-
Области памяти JVM. Heap (Eden/Survivor/Old — объекты), Stack (фреймы методов), Metaspace (метаданные классов, раньше PermGen), PC Register, Native Method Stack, CodeCache (JIT-скомпилированный код).
-
Контракт equals/hashCode. Если a.equals(b), то hashCode одинаков. Обратное необязательно. Рефлексивность, симметричность, транзитивность. equals(null) → false. Переопределяешь один — переопределяй оба. Нарушение ломает HashMap/HashSet.
-
Что сломается, если hashCode() — константа? Все объекты в одном bucket. Java 7: список O(n). Java 8+: при 8 коллизиях И capacity ≥ 64 — red-black tree O(log n). Деградация по сравнению с O(1).
-
String Pool и immutability. Pool в heap хранит уникальные литералы. new String("hi") — новый объект вне пула. intern() добавляет в пул. Immutability: безопасность, потокобезопасность, кэширование hashCode, переиспользование в пуле.
-
Generics: type erasure и PECS. Дженерики стираются компилятором (type erasure) — в рантайме нет информации о типовом параметре. PECS: Producer Extends, Consumer Super. List<? extends Number> — читать, List<? super Integer> — писать.
-
WeakReference / SoftReference / PhantomReference. Weak — GC собирает при первой же сборке. Soft — GC собирает при нехватке памяти (для кэшей). Phantom — для постфинализационной очистки ресурсов. WeakHashMap — ключи через WeakReference.
-
Функциональные интерфейсы и Stream API. Один абстрактный метод (SAM). Function, Predicate, Consumer, Supplier, Comparator. Стрим: промежуточные (ленивые) → терминальные (запускают конвейер). Обработка вертикальная, не горизонтальная. Стрим одноразовый.
-
Optional — когда? Для возвращаемых значений. НЕ для полей, параметров, коллекций. Антипаттерны: get() без isPresent(), Optional в полях, Optional
- . orElse vs orElseGet (ленивый).
-
final: класс, метод, поле. Класс — нельзя наследовать. Метод — нельзя переопределить. Поле — нельзя переприсвоить (но мутабельное содержимое можно менять). effectively final — для лямбд.
-
Абстрактный класс vs интерфейс. Абстрактный: состояние, конструкторы, один наследник. Интерфейс: контракт, множественная реализация. Java 8 — default/static. Java 9 — private методы.
Задачи «Что выведет?»¶
Тест 1. Integer cache
Integer i1 = 127, i2 = 127;
System.out.println(i1 == i2); // ?
Integer i3 = 128, i4 = 128;
System.out.println(i3 == i4); // ?
Ответ: true, false. IntegerCache: -128..127. Для 127 — один объект, ссылки совпадают. Для 128 — два разных. Мораль: для объектов — equals(), никогда ==.
Тест 2. Stream — ленивость List.of(1,2,3,4,5).stream()
.map(x -> { System.out.print(x+" "); return x; })
.filter(x -> x > 2)
.map(x -> { System.out.print(x+" "); return x; })
.toList();
Ответ: 1 2 3 3 4 4 5 5. Стрим обрабатывает вертикально: каждый элемент проходит всю цепочку. 1 и 2 не проходят filter → печатаются раз. 3,4,5 проходят → дважды.
Тест 3. Передача по значению
Integer i = Integer.valueOf(1);
inc(i);
System.out.println(i); // ?
static void inc(Integer i) { i++; }
Ответ: 1. Java передаёт ссылки по значению. i++ создаёт новый Integer(2) и присваивает локальной переменной. Оригинал не меняется.
СОВЕТ. Лайфхак На вопрос про equals/hashCode приведи практический пример: «положить объект в HashSet, изменить поле в hashCode — объект потеряется, contains() вернёт false». Это показывает понимание, а не заученность.
4. Коллекции¶
HashMap — чемпион. По опыту кандидатов в Яндекс: «обсасывают мапу со всех сторон — устройство, коллизии, treeify, resize, ключи-массивы, что будет если положить и не найти».
-
Как устроен HashMap? Массив Node
[]. Размер — степень двойки (default 16). Индекс: (n-1) & hash(key), hash — XOR верхних 16 бит с нижними. Коллизии — связный список. Java 8+: при TREEIFY_THRESHOLD=8 И capacity ≥ 64 → red-black tree. При 6 → обратно (UNTREEIFY). -
load factor и resize. Порог 0.75. При size >= capacity * loadFactor — resize: массив вдвое + перехеширование ВСЕХ элементов. Совет: initialCapacity = expectedSize / 0.75 + 1.
-
HashMap vs ConcurrentHashMap. HashMap: не потокобезопасен, допускает null-ключ. ConcurrentHashMap: Java 7 — Segment-блокировки, Java 8+ — CAS + synchronized на головах бакетов. null запрещён. computeIfAbsent — атомарная операция.
-
ArrayList vs LinkedList. ArrayList: O(1) доступ, O(n) вставка в середину, дружелюбен к CPU-кэшу. LinkedList: O(1) вставка/удаление в начало, O(n) доступ. На практике ArrayList почти всегда лучше.
-
TreeMap vs LinkedHashMap. TreeMap: red-black tree, O(log n), ключи отсортированы. LinkedHashMap: HashMap + двусвязный список, порядок вставки. accessOrder=true → LRU-кэш.
-
Fail-fast итератор. ConcurrentModificationException при изменении коллекции не через итератор. modCount-счётчик. Не гарантирован в многопоточке. Альтернатива: CopyOnWriteArrayList (fail-safe).
-
Можно ли byte[] как ключ HashMap? Технически можно, но hashCode у массива — по адресу (identityHashCode), а equals — по ссылке. Два одинаковых по содержимому массива дадут разные хэши. Нужно обернуть в ByteBuffer или свой класс с переопределёнными equals/hashCode.
5. Многопоточность и JMM¶
В Яндексе многопоточность спрашивают глубоко — volatile vs synchronized давали на каждом собесе. JMM, happens-before, CAS, пулы потоков — обязательно.
-
synchronized. Захват монитора. Instance-метод → this. Static → Class. Блок → указанный объект. Reentrant (счётчик). Гарантирует: mutual exclusion + happens-before (видимость).
-
volatile. Видимость (запрет кэширования в регистрах/L1) + запрет reordering. НЕ гарантирует атомарность i++ (read-increment-write). Для атомарных — AtomicInteger/LongAdder.
-
happens-before. unlock → lock того же монитора. volatile write → read. Thread.start() → первая инструкция потока. Последняя инструкция → join(). final-поля видны после конструктора.
-
ConcurrentHashMap: Java 7 vs Java 8. Java 7: массив Segment (ReentrantLock), каждый сегмент — свой HashMap. Java 8: убрали Segment, CAS + synchronized на головах бакетов (лучше параллелизм). Treeify при 8 коллизиях.
-
ThreadPoolExecutor — параметры. corePoolSize, maxPoolSize, keepAliveTime, workQueue (LinkedBlockingQueue/ArrayBlockingQueue/SynchronousQueue), threadFactory, rejectedExecutionHandler (AbortPolicy/CallerRunsPolicy/DiscardPolicy/DiscardOldestPolicy).
-
CountDownLatch vs CyclicBarrier vs Semaphore. CountDownLatch: одноразовый счётчик, потоки ждут обнуления. CyclicBarrier: переиспользуемый, все потоки ждут друг друга. Semaphore: ограничение числа одновременных потоков (пул ресурсов).
-
Deadlock — как возникает? Два+ потока ждут друг друга. Условия Коффмана. Избежать: упорядочить захват мониторов, tryLock с таймаутом. Обнаружение: jstack, VisualVM, Thread.getState().
-
Реализуй ограниченную BlockingQueue. synchronized + wait/notify. while(!condition) wait() — не if! (spurious wakeups). put() ждёт если полная, take() ждёт если пустая. notifyAll() после каждой операции.
ЛОВУШКА · volatile counter++ volatile long counter; counter++ — НЕВЕРНО. Это три операции (read-increment-write), volatile не обеспечивает атомарность. Три корректных варианта: synchronized, AtomicLong.incrementAndGet(), LongAdder.increment().
6. Spring и Spring Boot¶
Spring — основной фреймворк Путешествий. Спрашивают глубоко: прокси, жизненный цикл бинов, автоконфигурация, AOP.
-
IoC и DI. IoC — контейнер управляет жизненным циклом. DI — зависимости внедряются извне. Три способа: конструктор (лучший), сеттер, поле (@Autowired). Constructor injection: поля final, явные зависимости, легко тестировать.
-
Жизненный цикл бина. BeanDefinition → инстанцирование → DI → Aware-интерфейсы → BPP.postProcessBefore → @PostConstruct → InitializingBean → init-method → BPP.postProcessAfter → ГОТОВ → @PreDestroy → DisposableBean → destroy-method. BPP — точка создания прокси.
-
Scope бинов. singleton (default), prototype, request, session, application, websocket. Инжект prototype в singleton: через Provider
, ObjectFactory или @Lookup. Иначе prototype создастся один раз. -
@Transactional — прокси. Spring создаёт прокси (JDK/CGLIB). Прокси открывает транзакцию, вызывает метод, commit/rollback. self-call минует прокси → @Transactional/@Cacheable/@Async не работают. Решение: вынести в другой бин.
-
Автоконфигурация Spring Boot. @EnableAutoConfiguration + @Conditional. Если DataSource в classpath — автоматически настроит JPA. Список: META-INF/spring/...AutoConfiguration.imports (Spring Boot 3+, раньше spring.factories).
-
@SpringBootApplication. @Configuration + @EnableAutoConfiguration + @ComponentScan.
-
AOP: JDK proxy vs CGLIB. JDK dynamic proxy — если бин реализует интерфейс (через Proxy.newProxyInstance). CGLIB — наследование от класса (final-классы нельзя проксировать). Spring Boot 3 по умолчанию использует CGLIB.
-
Обработка исключений. @RestControllerAdvice + @ExceptionHandler. ErrorDto: code, message, timestamp. HTTP-статусы: 400 (валидация), 404, 409 (конфликт), 500.
7. Hibernate и JPA¶
-
Состояния сущности. Transient → Persistent (persist) → Detached (close/detach) → Removed (remove). Persistent: dirty checking — изменения автоматически синхронизируются.
-
N+1 проблема. 1 findAll() + N доп. запросов на lazy-коллекции. Решения: JOIN FETCH, @EntityGraph, @BatchSize(100), DTO-проекция. EAGER — неправильный ответ.
-
LazyInitializationException. Обращение к lazy-полю после закрытия сессии. Решения: JOIN FETCH, @EntityGraph, DTO. OpenSessionInView — анти-паттерн.
-
Оптимистичная блокировка. @Version (int/long). UPDATE ... WHERE version = ?. Не совпала → OptimisticLockException. Для low-contention (веб-приложения). Пессимистичная: SELECT FOR UPDATE.
-
JOOQ vs Hibernate. JOOQ: типизированный SQL, compile-time проверка запросов, нет dirty checking. Hibernate: ORM, lazy loading, кэширование. В Путешествиях используют оба — JOOQ для сложных запросов, Hibernate для CRUD.
-
Кэши Hibernate. First-level: привязан к Session, автоматический. Second-level: общий (EhCache, Caffeine), опциональный. Query cache: кэширует JPQL-результаты.
ЛОВУШКА · EAGER = решение N+1? Нет. EAGER грузит коллекцию ВСЕГДА, даже когда она не нужна — медленнее и съедает память. Правильно: LAZY по умолчанию + FETCH/EntityGraph там, где нужны данные.
8. SQL и PostgreSQL¶
SQL спрашивают отдельной задачей — пишешь запросы вживую. Оконные функции, EXPLAIN ANALYZE, уровни изоляции — обязательно для Яндекса.
-
ACID. Atomicity (целиком/никак), Consistency (валидное состояние), Isolation (изоляция транзакций), Durability (после COMMIT данные сохранятся — WAL в PostgreSQL).
-
Уровни изоляции. READ_UNCOMMITTED, READ_COMMITTED (default PG), REPEATABLE_READ, SERIALIZABLE. Аномалии: dirty read, non-repeatable read, phantom read, serialization anomaly.
-
MVCC в PostgreSQL. Каждая строка: xmin (создавшая транзакция), xmax (удалившая). Читатели не блокируют писателей. Старые версии чистит VACUUM. Проблема: table bloat.
-
Типы индексов PostgreSQL. B-tree (default), Hash (=), GIN (JSONB, full-text), GiST (геометрия), BRIN (большие таблицы с порядком). Покрывающий индекс (INCLUDE) — Index Only Scan.
-
Оконные функции. ROW_NUMBER(), RANK(), DENSE_RANK(), LAG/LEAD, SUM/AVG OVER (PARTITION BY ... ORDER BY ...). Агрегация без GROUP BY — строки не сворачиваются.
-
EXPLAIN ANALYZE. Seq Scan (плохо на большой таблице), Index Scan (хорошо), Bitmap Heap Scan. Rows Removed by Filter — индекс не помогает. estimated ≠ actual → ANALYZE.
SQL-задачи из собесов Яндекса¶
Вторая по величине зарплата (3 способа) -- Способ 1: вложенный MAX
SELECT MAX(salary) FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);
-- Способ 2: DISTINCT + OFFSET
SELECT DISTINCT salary FROM employees
ORDER BY salary DESC LIMIT 1 OFFSET 1;
-- Способ 3: DENSE_RANK
SELECT salary FROM (
SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rk
FROM employees) t WHERE rk = 2;
Топ-5 артикулов в каждом регионе
SELECT * FROM (
SELECT region, article, sales,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY sales DESC) AS rn
FROM sales_data
) t WHERE rn <= 5;
9. Алгоритмы — главный фильтр¶
Алгоритмическая секция — самая обсуждаемая часть. 2–3 задачи за 1 час, решения в 10–30 строк. Код пишется в Yandex.Code — без компиляции, без автокомплита. Ключевое: умение «прогнать» код в голове.
Что НЕ спрашивают (официально)¶
-
Динамическое программирование
-
Алгоритм Дейкстры
-
KMP (поиск подстроки)
-
Собственные реализации куч
-
Сложные графовые алгоритмы
Что спрашивают (по отзывам)¶
-
Хэш-таблицы: Two Sum, подсчёт частот, анаграммы, группировка
-
Два указателя: сжатие интервалов, удаление дубликатов, палиндромы
-
Sliding window: детектор роботов (>5000 событий/час через Deque)
-
BFS/DFS: Number of Islands, обход графа/дерева
-
Сортировка + бинарный поиск: Search in Rotated Sorted Array
-
Стек/очередь: скобочные последовательности, монотонный стек
-
Строки: Run-Length encoding (aaaffccccd → 3a2f4cd)
-
Слияние: Merge k sorted arrays через PriorityQueue
Реальные задачи из собесов Яндекса¶
Задача 1. Сжатие в диапазоны Дан массив [1,4,5,2,3,9,8,11,0]. Вернуть строку "0-5,8-9,11".
public String compressRanges(int[] arr) {
Arrays.sort(arr);
StringBuilder sb = new StringBuilder();
int i = 0;
while (i < arr.length) {
int start = arr[i];
while (i + 1 < arr.length && arr[i+1] == arr[i] + 1) i++;
if (arr[i] == start) sb.append(start);
else sb.append(start).append('-').append(arr[i]);
sb.append(',');
i++;
}
sb.setLength(sb.length() - 1);
return sb.toString();
}
O(n log n) из-за сортировки. Два указателя для расширения диапазона.
Задача 2. Run-Length Encoding
public String rle(String s) {
StringBuilder sb = new StringBuilder();
int i = 0;
while (i < s.length()) {
char c = s.charAt(i);
int count = 0;
while (i < s.length() && s.charAt(i) == c) {
count++; i++;
}
if (count > 1) sb.append(count);
sb.append(c);
}
return sb.toString();
}
aaaffccccd → 3a2f4cd. O(n) времени и памяти.
Задача 3. Merge Intervals
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> result = new ArrayList<>();
int[] current = intervals[0];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] <= current[1]) {
current[1] = Math.max(current[1], intervals[i][1]);
} else {
result.add(current);
current = intervals[i];
}
}
result.add(current);
return result.toArray(new int[0][]);
}
O(n log n). Сортировка по началу + жадное слияние.
Задача 4. Детектор роботов (sliding window) Дан поток событий (userId, timestamp). Найти пользователей с >5000 событиями за любой час.
Map<Long, Deque<Long>> windows = new HashMap<>();
void processEvent(long userId, long timestamp) {
windows.computeIfAbsent(userId, k -> new ArrayDeque<>());
Deque<Long> deque = windows.get(userId);
deque.addLast(timestamp);
while (deque.peekFirst() < timestamp - 3600) {
deque.pollFirst();
}
if (deque.size() > 5000) {
markAsBot(userId);
}
}
ФИШКА. Тренировочный контест Официальный контест Яндекса — contest.yandex.ru/contest/8458. 6 задач: камни, последовательные единицы, дубликаты, скобки, анаграммы, слияние k массивов. Прорешай его перед собесом.
10. System Design¶
Для Middle секция необязательна, но сильный результат поднимет грейд (Middle → Middle+). Для Senior — ключевая. Кандидат ведёт обсуждение, интервьюер задаёт наводящие вопросы.
Что оценивают¶
-
Уточнение требований: функциональные + нефункциональные (RPS, SLA, объёмы)
-
Back-of-envelope расчёты (сколько запросов, сколько данных, сколько серверов)
-
High-level архитектура (компоненты, потоки данных, API)
-
Обоснование выбора технологий (SQL vs NoSQL, sync vs async)
-
Масштабирование и шардирование
-
Отказоустойчивость и консистентность
Реальные задачи из Яндекса¶
-
Сервис коротких ссылок. base62 с 62^7 ≈ 3.5 трлн. Redis-кэш популярных. Расчёт: чтения/записи 100:1. Шардирование по хэшу ключа.
-
Распределённый rate limiter. Token Bucket vs Sliding Window Log. Redis + Lua-скрипт для атомарности. Обсудить: точность vs производительность.
-
Яндекс Go / такси-сервис. Геоиндекс (Geohash/H3), matching-сервис, партиционирование по городам, eventual consistency геопозиций.
-
Client-side Load Balancer. Round-robin → retry → circuit breaker. Обсудить: health checks, backoff, fallback. Реальная задача Middle в Путешествиях.
-
Сервис бронирования отелей. Поиск с динамическими ценами. Outbox-паттерн для финансовых операций. Идемпотентность платежей. Партнёрские интеграции через API Gateway.
11. Docker, Kubernetes, CI/CD¶
Яндекс использует свои инструменты деплоя (Nanny / Yandex Deploy), но принципы контейнеризации и CI/CD спрашивают. Docker и K8s — must-have знание.
-
Образ vs контейнер. Образ — неизменяемый шаблон из слоёв. Контейнер — запущенный экземпляр с writable-слоем.
-
Multi-stage build. Сборка (Maven/Gradle) → финальный образ только JAR. ~200 MB вместо ~800 MB. Безопаснее, быстрее.
-
Kubernetes: Pod, Deployment, Service. Pod — 1+ контейнер. Deployment — реплики, rolling update. Service — стабильный endpoint (ClusterIP/NodePort/LoadBalancer). Ingress — L7.
-
Liveness vs Readiness probe. Liveness: жив ли? Нет → перезапуск. Readiness: готов к трафику? Нет → убирается из балансировки. Spring Boot Actuator: /health/liveness, /health/readiness.
-
CI/CD pipeline. checkout → build → test → docker build → push registry → deploy staging → (approval) → prod. В Яндексе: Arcadia CI + Nanny для деплоя.
12. Практические задачи (live-coding)¶
Помимо алгоритмов, на собесе в Яндекс могут попросить написать REST-контроллер, unit-тест, или решить задачу на Stream API. С 2025 года — секция Advanced Code: IDE + автотесты + интернет.
Задача 1. Группировка Stream API¶
// Отдел → список сотрудников Map
.collect(Collectors.groupingBy(Employee::getDepartment));
// Отдел → средняя зарплата Map
Collectors.averagingDouble(Employee::getSalary)));
Задача 2. Потокобезопасный LRU-кэш¶
public class LRUCache<K, V> {
private final int capacity;
private final Map<K, V> map;
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new LinkedHashMap<>(capacity, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> e) {
return size() > capacity;
}
};
}
public synchronized V get(K key) { return map.get(key); }
public synchronized void put(K key, V value) { map.put(key, value); }
}
LinkedHashMap с accessOrder=true + removeEldestEntry. synchronized для потокобезопасности. Для высокой нагрузки — Caffeine.
Задача 3. REST-контроллер¶
@RestController
@RequestMapping("/api/bookings")
public class BookingController {
private final BookingService bookingService;
public BookingController(BookingService bookingService) {
this.bookingService = bookingService;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public BookingDto create(@RequestBody @Valid CreateBookingRequest req) {
return bookingService.create(req);
}
@GetMapping("/{id}")
public BookingDto getById(@PathVariable Long id) {
return bookingService.findById(id);
}
}
Задача 4. Unit-тест Mockito + AssertJ¶
@ExtendWith(MockitoExtension.class)
class BookingServiceTest {
@Mock BookingRepository repo;
@Mock PaymentGateway gateway;
@InjectMocks BookingService service;
@Test
void shouldCreateBookingAndCharge() {
// given
var req = new CreateBookingRequest("hotel-1", LocalDate.now());
var saved = new Booking(1L, "hotel-1", Status.CONFIRMED);
when(repo.save(any())).thenReturn(saved);
// when
var result = service.create(req);
// then
assertThat(result.getStatus()).isEqualTo(Status.CONFIRMED);
verify(gateway).charge(any());
}
}
Задача 5. Code Review — найди проблемы¶
public class UserCache {
private static Map<Long, User> cache = new HashMap<>();
public static User getUser(Long id) {
if (cache.containsKey(id)) {
return cache.get(id);
}
User user = loadFromDb(id);
cache.put(id, user);
return user;
}
}
Проблемы: 1) HashMap не потокобезопасен → ConcurrentHashMap. 2) containsKey+get → computeIfAbsent. 3) Кэш бесконечный → memory leak. 4) static → тяжело тестировать. 5) null от loadFromDb.
13. План подготовки + чек-лист¶
За 3–4 недели¶
-
Прорешать contest.yandex.ru/contest/8458 — официальный контест Яндекса
-
30–50 задач LeetCode Easy+Medium: hash-table, two-pointers, sliding-window, BFS/DFS
-
Бесплатный курс start.practicum.yandex/algorithms-interview
-
Повторить Java Concurrency in Practice (Гётц) — volatile, synchronized, CAS, пулы
-
Поднять проект Spring Boot 3 + PostgreSQL + gRPC + тесты
За неделю¶
-
2–3 мок-интервью: pramp.com, interviewing.io или друзья
-
Прорешать все вопросы из гайда ВСЛУХ — мысли в голове ≠ слова
-
Подготовить 2–3 проекта по STAR: проблема → что сделал → результат
-
Потренироваться писать код БЕЗ IDE — именно так будет на секции
-
Решить 5+ задач на бумаге/в блокноте и «прогнать» в голове
В день собеса¶
-
Камера, микрофон, интернет — проверить за 30 минут
-
Yandex.Code не поддерживает автокомплит — привыкни заранее
-
Рассуждать вслух — молчание хуже «дай подумать»
-
Сначала — план решения, потом — код. Не бросайся писать сразу
-
Обязательно «прогони» код на примере — баги в голове = отказ
-
2–3 вопроса в конце: про команду, проект, стек, буткемп
ВНИМАНИЕ · Главная причина отказов Не незнание теории, а невнятное изложение решения и баги, которые кандидат не замечает при прогоне кода в голове. Тренируйтесь писать и проверять код без IDE!
Финальный чек-лист¶
| Блок | Готов, если можешь... |
|---|---|
| Java Core | equals/hashCode на примере + Integer cache + |
generics PECS
Коллекции HashMap в Java 8+: treeify, resize, mutable-ключ, byte[] как ключ
Многопоточность BlockingQueue через wait/notify + volatile vs synchronized + CAS
JMM happens-before на 5+ примерах, почему volatile counter++ некорректно
Spring Жизненный цикл бина наизусть + self-call + JDK vs CGLIB прокси
Hibernate N+1 → JOIN FETCH / @EntityGraph / DTO. JOOQ vs Hibernate — когда что
SQL Оконные функции + EXPLAIN ANALYZE + вторая зарплата 3 способами
Алгоритмы 3 задачи за 1 час в текстовом редакторе БЕЗ компиляции
System Design Short URL или Rate Limiter с расчётами RPS и выбором технологий
Live-coding LRU-кэш + REST-контроллер + code review за 15 минут каждое
Удачи на собесе!
// git push origin offer
Что дальше¶
- Повторить: алгоритмы, Java/Spring backend, SQL, System Design, проверку решений без IDE.
- Спросить в канале: свежие вопросы Яндекса, задачи из секций и опыт прохождения Advanced Code.
- Получать новые разборы: @java_jub.
- Проверить знания: тесты JavaJub.