Stream API
29 питань і відповідей у розділі Stream API.
Питання цього розділу
- Які переваги дає використання Stream API?
- В чому різниця між intermediate та terminal операціями?
- Що робить операція filter()?
- Що робить операція map()?
- Що робить операція collect()?
- Що таке Collector і які є вбудовані Collectors?
- Що робить операція flatMap()?
- В чому різниця між map() та flatMap()?
- Що таке паралельні стріми?
- Коли використовувати parallel streams?
- Як створити parallel stream?
- Які потенційні проблеми можуть бути з паралельними стрімами?
- Що таке ForkJoinPool і як він пов’язаний з parallel streams?
- Чи можна змінювати стан зовнішніх змінних в Stream операціях?
- Що таке побічні ефекти (side effects) в Stream?
- Чому слід уникати побічних ефектів в Stream?
- Що робить операція reduce()?
- В чому різниця між reduce() та collect()?
- Що таке операція peek() і коли її використовувати?
- Чи можна повторно використовувати Stream?
- Що таке lazy evaluation в Stream?
- Коли починається виконання операцій в Stream?
- Що роблять операції distinct(), sorted(), limit(), skip()?
- Як працює коротке замикання (short-circuiting) в Stream?
- Що таке операції anyMatch(), allMatch(), noneMatch()?
- Що роблять операції findFirst() та findAny()?
- Як зібрати Stream в Map?
- Що робити при колізіях ключів при збірці в Map?
- Як працювати з Optional в Stream?
Навігатор по розділу
29 запитань для підготовки до співбесіди на Middle Java Developer.
📋 Всі запитання
🗺️ Карта залежностей тем
┌──────────────────────────────────────────┐
│ STREAM BASICS (1-2) │
│ 1. Переваги Stream API │
│ 2. Intermediate vs Terminal │
└──────────────────┬───────────────────────┘
│
┌──────────────────────────┼──────────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌────────────────────┐
│ CORE OPS │ │ COLLECT & │ │ PARALLEL │
│ (3-8) │ │ REDUCE (5-8) │ │ (9-13) │
│ 3. filter() │ │ 5. collect() │ │ 9. Parallel streams│
│ 4. map() │ │ 6. Collector │ │ 10. Коли використо│
│ 7. flatMap() │ │ 7. flatMap() │ │ 11. Як створити │
│ 8. map vs fmap │ │ 8. map vs fmap│ │ 12. Проблеми │
│ │ │ 17. reduce() │ │ 13. ForkJoinPool │
│ │ │ 18. reduce vs │ │ │
│ │ │ collect │ │ │
└───────┬───────┘ └───────┬───────┘ └────────┬───────────┘
│ │ │
└────────────────────────┼────────────────────────┘
▼
┌──────────────────────────────────────────┐
│ CONCEPTS & PITFALLS (14-22) │
│ 14. Зовнішні змінні │
│ 15-16. Побічні ефекти │
│ 19. peek() │
│ 20. Повторне використання │
│ 21-22. Lazy evaluation │
└──────────────────────────────────────────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌────────────────────┐
│ ADVANCED OPS │ │ MAP & OPT │ │ │
│ (23-26) │ │ (27-29) │ │ │
│ 23. distinct, │ │ 27. toMap │ │ │
│ sorted, │ │ 28. Колізії │ │ │
│ limit, skip│ │ 29. Optional │ │ │
│ 24. Short-cir│ │ │ │ │
│ 25. match() │ │ │ │ │
│ 26. findFirst │ │ │ │ │
└───────────────┘ └───────────────┘ └────────────────────┘
🎯 Рекомендований порядок вивчення
🟢 Рівень Junior (тижні 1-2)
| Крок | Тема | Файли | Мета |
|---|---|---|---|
| 1 | Stream основи | Q1, Q2 | Що таке Stream, lazy, intermediate vs terminal |
| 2 | Основні операції | Q3, Q4 | filter(), map() |
| 3 | collect() | Q5 | toList(), toSet(), joining() |
| 4 | map vs flatMap | Q7, Q8 | Коли map, коли flatMap |
| 5 | peek, reuse | Q19, Q20 | peek для налагодження, не можна перевикористати |
| 6 | Lazy evaluation | Q21, Q22 | Коли починається виконання |
🟡 Рівень Middle (тижні 3-4)
| Крок | Тема | Файли | Мета |
|---|---|---|---|
| 1 | Collector deep dive | Q6 | characteristics, custom Collector, teeing |
| 2 | reduce vs collect | Q17, Q18 | Immutable vs mutable reduction |
| 3 | Parallel streams | Q9, Q10, Q11 | Коли parallel, коли sequential |
| 4 | Problems with parallel | Q12, Q13 | Cache locality, ForkJoinPool, work-stealing |
| 5 | Side effects | Q14, Q15, Q16 | Чому уникати, stateful ops |
| 6 | Advanced ops | Q23, Q24, Q25 | stateful ops, short-circuit, match |
| 7 | findFirst/findAny | Q26 | Ordered vs unordered, parallel impact |
🔴 Рівень Senior (тижні 5-6)
| Крок | Тема | Файли | Мета |
|---|---|---|---|
| 1 | ForkJoinPool internals | Q13 (Senior) | Deque, LIFO/FIFO, work-stealing algorithm |
| 2 | Collector internals | Q6 (Senior) | Supplier/Accumulator/Combiner/Finisher, characteristics |
| 3 | Parallel pitfalls | Q12 (Senior) | Cache thrashing, ThreadLocal, boxed primitives |
| 4 | Short-circuit optimization | Q24 (Senior) | Java 9+ count() optimization, SIZED sources |
| 5 | Map collection pitfalls | Q27, Q28 | toMap vs groupingBy, merge function, parallel combiner |
| 6 | Optional in Stream | Q29 | orElse vs orElseGet, flatMap with Optional |
| 7 | Performance tuning | Q9-Q13 (Senior) | N×Q > 10,000 rule, unordered optimization, custom FJP |
🔗 Ключові зв’язки між темами
Тема: Основні операції
Q1 (Stream API) → Q2 (Intermediate vs Terminal) → Q3 (filter) → Q4 (map)
↓ ↓
Q7 (flatMap) ←────────────────────────────── Q8 (map vs flatMap)
Ключові зв’язки:
- Q2 ↔ Q21: Intermediate операції ліниві, виконуються лише при terminal
- Q3 ↔ Q24: filter — short-circuit операція, може зупинити пайплайн рано
- Q4 ↔ Q7: map = 1→1, flatMap = 1→N + flatten
- Q8 ↔ Q17: map трансформує, reduce/aggregates — згортає
Тема: Collect та Reduce
Q5 (collect) → Q6 (Collector) → Q17 (reduce) → Q18 (reduce vs collect)
↓ ↓
Q27 (toMap) → Q28 (Колізії ключів)
Ключові зв’язки:
- Q5 ↔ Q6: collect приймає Collector — «рецепт» збирання
- Q6 ↔ Q18: Collector = mutable reduction, reduce = immutable reduction
- Q17 ↔ Q18: reduce для простих значень, collect для колекцій
- Q27 ↔ Q28: toMap кидає при дублікатах → merge function
Тема: Паралелізм
Q9 (Parallel streams) → Q10 (Коли використовувати) → Q11 (Як створити)
↓ ↓
Q12 (Проблеми) ←──── Q13 (ForkJoinPool)
Ключові зв’язки:
- Q9 ↔ Q13: parallelStream = ForkJoinPool.commonPool()
- Q10 ↔ Q12: Коли parallel допомагає vs коли заважає
- Q12 ↔ Q14: Side effects в parallel → race conditions
- Q13 ↔ Q24: work-stealing алгоритм, LIFO/FIFO deque
Тема: Просунуті операції
Q23 (distinct/sorted/limit/skip) → Q24 (Short-circuit)
↓ ↓
Q25 (anyMatch/allMatch/noneMatch) → Q26 (findFirst/findAny)
↓
Q29 (Optional)
Ключові зв’язки:
- Q23 ↔ Q24: limit/skip/findFirst — short-circuit операції
- Q24 ↔ Q25: match-операції = short-circuit terminal
- Q25 ↔ Q26: anyMatch ≈ findAny().isPresent(), але без Optional overhead
- Q26 ↔ Q29: findFirst/findAny повертають Optional
🎓 Шпаргалка: що знати для кожного рівня
🟢 Junior
- Stream ≠ Collection: Stream не зберігає дані, це конвеєр операцій
- Intermediate (filter, map, flatMap) — ліниві, terminal (collect, forEach, count) — запускають
- filter = відсів, map = перетворення 1→1, flatMap = 1→N + flatten
- collect = зібрати результат в List/Map/Set
- Stream не можна перевикористати — після terminal операції він вичерпаний
🟡 Middle
- Lazy evaluation: операції виконуються element-by-element, не stage-by-stage
- Stateful операції (distinct, sorted) потребують знання всіх елементів — вбивають паралелізм
- Short-circuit (findFirst, anyMatch, limit) — можуть зупинити пайплайн рано
- parallelStream = ForkJoinPool.commonPool(), cores-1 воркерів
- Side effects в Stream → баги в parallelStream, використовуйте collect замість
- reduce (immutable) vs collect (mutable) — collect ефективніше для колекцій
🔴 Senior
- ForkJoinPool: per-thread Deque, LIFO для власника, FIFO для злодіїв
- Spliterator характеристики: SIZED критично для parallel (точний split)
- N × Q > 10,000 правило: коли parallel окупається
- Java 9+: count() оптимізований для SIZED джерел — peek не виконається
- orElse (eager) vs orElseGet (lazy) — orElseGet для дорогих дефолтів
- Cache thrashing в parallel: потоки пишуть в одну cache line → деградація
- Custom ForkJoinPool для ізоляції, але не для I/O (використовуйте Virtual Threads)
📝 Формат кожного файлу
Кожен файл містить:
- 🟢 Junior Level — базове розуміння, прості аналогії, приклади
- 🟡 Middle Level — внутрішності, типові помилки, практичні приклади
- 🔴 Senior Level — deep dive, edge cases, production досвід, моніторинг
- 🎯 Шпаргалка для інтерв’ю — ключові тези, часті запитання, червоні прапорці, пов’язані теми