Питання 24 · Розділ 8

Як працює коротке замикання (short-circuiting) в Stream?

У Sink є метод cancellationRequested():

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Short-circuiting (коротке замикання) — стрим зупиняється, щойно результат визначений, не обробляючи решту елементів.

Аналогія: як && у Java: false && expensiveCheck()expensiveCheck() не виконається, бо результат вже визначений (false). Аналог break у циклі.

Terminal-операції з коротким замиканням:

  • anyMatch() — зупиняється при першому true
  • allMatch() — зупиняється при першому false
  • noneMatch() — зупиняється при першому true
  • findFirst() / 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()]]