Як працює коротке замикання (short-circuiting) в Stream?
У Sink є метод cancellationRequested():
🟢 Junior Level
Short-circuiting (коротке замикання) — стрим зупиняється, щойно результат визначений, не обробляючи решту елементів.
Аналогія: як && у Java: false && expensiveCheck() — expensiveCheck() не виконається, бо результат вже визначений (false). Аналог break у циклі.
Terminal-операції з коротким замиканням:
anyMatch()— зупиняється при першомуtrueallMatch()— зупиняється при першомуfalsenoneMatch()— зупиняється при першомуtruefindFirst()/findAny()— зупиняється при першому елементіlimit(n)— зупиняється після n елементів
// Зупиниться після першого знайденого
boolean hasAdult = persons.stream()
.anyMatch(p -> p.getAge() >= 18);
// Візьме тільки перші 5
List<String> first5 = list.stream().limit(5).collect(toList());
🟡 Middle Level
Внутрішній механізм: прапорець cancellationRequested
У Sink є метод cancellationRequested():
- Terminal-операції виставляють його в
trueпісля знаходження результату - Intermediate-операції перевіряють прапорець перед запитом наступного елемента
- Spliterator припиняє обхід даних
Види короткого замикання
Java 9+:
count()на SIZED джерелах (ArrayList, масив) оптимізований — повертає розмір без прогону через пайплайн. Це означає, що filter/map ДО count() можуть НЕ виконатися!
Terminal:
Зупиняють весь конвеєр при досягненні умови (anyMatch, findFirst)
Intermediate:
Обмежують обсяг даних (limit(n)) — перетворюють нескінченний стрим на скінченний
Робота з нескінченними стримами
Stream.iterate(0, n -> n + 1) // Нескінченно
.filter(n -> n % 2 == 0)
.limit(10) // Обмежує
.collect(toList()); // Завершується успішно
Важливо: Якщо переставити filter та limit неправильно — можна отримати нескінченний цикл.
🔴 Senior Level
Паралелізм та Short-circuiting
У паралельних стримах працює менш ефективно:
- Кілька потоків можуть продовжувати обчислення після знаходження результату
limit()вимагає складної координації (буферизація з різних потоків)- Паралельний стрим може стати повільнішим за послідовний
Wait Cost
У розподілених системах findAny() пріоритетніше за findFirst() — повертає результат від найшвидшого потоку, не чекаючи елементів із черги.
Infinite loop detection
Якщо CPU завантажений на 100% і стрим не завершується — перевірте умови короткого замикання. Додайте peek() перед short-circuit операцією — побачите, скільки елементів реально перевірено.
Short-circuit vs Loops
Коротке замикання в стримах працює так само ефективно, як break або return у звичайному циклі for.
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
- Short-circuiting — стрим зупиняється, щойно результат визначений, не обробляючи решту елементів
- Terminal short-circuit: anyMatch, allMatch, noneMatch, findFirst, findAny
- Intermediate short-circuit: limit(n) — перетворює нескінченний стрим на скінченний
- Механізм: прапорець
cancellationRequested()у Sink, Spliterator припиняє обхід - У parallelStream short-circuit працює менш ефективно — кілька потоків можуть продовжувати обчислення
findAny()пріоритетніше заfindFirst()у паралельних стримах — не чекає «найлівіший» елемент- anyMatch/limit — ключові інструменти для роботи з нескінченними стримами
- Java 9+: count() на SIZED джерелах оптимізований — filter/map ДО count() можуть НЕ виконатися
Часті уточнюючі запитання:
- Як працює коротке замикання всередині? — Terminal-операція виставляє
cancellationRequested = true, Spliterator припиняє запитувати елементи. - Чому findAny швидший за findFirst у parallelStream? — findFirst вимагає координації потоків для гарантії порядку; findAny повертає результат від найшвидшого потоку.
- Чи може limit() працювати з нескінченним стримом? — Так, limit(n) — єдиний спосіб завершити нескінченний стрим, але він має стояти до важких операцій.
- Чому parallelStream може стати повільнішим при short-circuit? — Кілька воркерів продовжують роботу після знаходження результату, overhead на скасування задач.
Червоні прапорці (НЕ говорити):
- «Short-circuit обробляє всі елементи» — неправда, в цьому і суть короткого замикання
- «findFirst та findAny однакові в parallelStream» — неправда, findFirst дорогий через координацію
- «limit() після sorted() — гарна ідея» — неправда, сортування всього набору перед обмеженням надлишкове
- «parallelStream завжди швидший для short-circuit» — неправда, може бути повільнішим через координацію
Пов’язані теми:
- [[21. Що таке lazy evaluation в Stream]]
- [[22. Коли починається виконання операцій в Stream]]
- [[25. Що таке операції anyMatch(), allMatch(), noneMatch()]]
- [[26. Що роблять операції findFirst() та findAny()]]