Ответ
Да, Stream по умолчанию является однопоточным (последовательным).
Поток становится параллельным только при явном вызове метода parallel().
Пример последовательного (однопоточного) Stream:
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.stream() // Создаёт последовательный поток
.map(n -> n * 2) // Операции выполняются в одном потоке
.forEach(System.out::println); // Порядок элементов гарантирован
Переключение режимов:
stream.parallel() // Переключает поток в параллельный режим
stream.sequential() // Возвращает поток в последовательный режим
Почему это важно:
- Последовательный поток предсказуем и прост для отладки.
- Параллельный поток (
parallel()) используетForkJoinPoolи может ускорить обработку больших данных, но добавляет накладные расходы и может нарушить порядок элементов. - Последний вызванный метод (
parallel()илиsequential()) определяет режим для всего конвейера.
Ответ 18+ 🔞
Да, ну вот, смотри, классическая история, как всегда: все думают, что Stream — это такой волшебный параллельный унитаз, куда можно скинуть данные и они сами разбегутся по ядрам. Ан нет, ёпта!
По умолчанию-то он, этот Stream, скромный такой, однопоточный, последовательный. Сидит себе в одном потоке и тихонечко, по порядку, всё обрабатывает. Как бухгалтер в районной конторе — без суеты.
Вот смотри, пример, чтоб совсем понятно было:
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.stream() // Вот он, наш скромняга. Никакого параллелизма, чистая последовательность.
.map(n -> n * 2) // Каждое число умножается по очереди, в одном потоке.
.forEach(System.out::println); // И выводится тоже по порядку. Гарантированно!
А чтобы этот тихоня превратился в буйного многопоточного монстра, который будет рвать данные на куски и обрабатывать их одновременно — ему нужно явно дать пинка под зад методом parallel(). Без команды он сам не начнёт!
И, внимание, сюжетный поворот! Режим можно и туда, и обратно переключать:
stream.parallel() // Вжух! И поток стал параллельным, начал использовать ForkJoinPool.
stream.sequential() // А теперь опять стал последовательным, успокоился.
Но тут главное не обосраться с логикой. Какой метод вызвал последним — тот и рулит балом для всего конвейера. Сначала сделал parallel(), потом sequential() — и всё, пидарас шерстяной, поток опять стал однопоточным.
А почему это, блядь, важно?
- Последовательный поток — это предсказуемость и простота. Порядок есть, отладка проще. Для него не нужны танцы с бубном.
- Параллельный поток (
parallel()) — это как попытка ускорить стройку, наняв овердохуища народу. На больших объёмах может помочь, но пока ты их соберёшь, распишешь задачи и потом результаты склеишь — накладные расходы съедят всю выгоду. Да ещё и порядок элементов может нарушиться, если неаккуратно.
Так что, блядь, думай головой. Не лепи parallel() на каждую коллекцию из трёх элементов — только себе дороже выйдет. Волшебной таблетки нет, есть инструмент, который нужно применять с умом.