Фильтр выбирает, преобразование меняет
Операция filter оставляет элементы, для которых условие истинно. Операция map применяет преобразование к каждому проходящему элементу. Возьмём числа 1, 2, 3, 4. Если сначала оставить чётные, останутся 2 и 4; прибавление единицы даст 3 и 5. Если сначала прибавить единицу ко всем значениям, получится 2, 3, 4, 5; фильтр чётности оставит 2 и 4. Названия операций одинаковы, но условие проверяется уже на разных значениях. Поэтому перестановка меняет смысл задачи.
Описание обработки и её выполнение различаются
Промежуточные операции создают этапы конвейера и выполняются лениво: добавление filter не означает немедленного обхода всех данных. Обход запускается терминальной операцией. Для потока целых чисел IntStream такой операцией может быть sum, возвращающая сумму. После завершения использованный поток нельзя обходить повторно; для новой обработки получают новый поток из подходящего источника. Это отличается от бумажного списка, который можно перечитывать сколько угодно. Сохранённая переменная со Stream не превращается в копию всех результатов.
Функция должна оставаться понятной
Прибавление единицы в примере получает число и возвращает новое значение. Оно не дописывает элементы в общий список и не меняет счётчик где-то снаружи. Такой способ описания проще рассматривать как преобразование данных. Если же результат зависит от постоянно меняющейся внешней переменной, одной схемы конвейера уже недостаточно для понимания. Особенно заметны трудности при параллельной обработке. Сначала полезно проверить логику на небольшом наборе значений, а производительность оценивать отдельно от правильности результата.
Попробуйте на практике
Разберите две версии числового конвейера на бумаге. Среда разработки и установка Java не требуются.
- Запишите исходный ряд 1, 2, 3, 4. Для версии А выполните отбор чётных значений, затем прибавьте единицу к каждому оставшемуся числу. Сохраните промежуточный ряд.
- Для версии Б начните заново с исходных данных. Сначала прибавьте единицу к каждому числу, затем оставьте только чётные значения. Не используйте результат версии А как источник.
- Сложите значения каждой итоговой последовательности. Подпишите, какая операция соответствует filter, какая map и какая терминальному sum для IntStream.
- Объясните разницу одним предложением: в какой момент каждая версия проверяет чётность. Отдельно отметьте, что для двух реальных запусков потребовались бы два отдельных потока.
Как проверить результат. Версия А даёт числа 3 и 5 с суммой 8. Версия Б даёт 2 и 4 с суммой 6. Объяснение должно связывать отличие с порядком преобразования и отбора, а не с непредсказуемостью Java. Исходный набор чисел в обеих версиях одинаков.
Частые вопросы
Означает ли использование Stream автоматическую многопоточность?
Нет. Поток данных может обрабатываться последовательно. Параллельный режим является отдельным выбором и не гарантирует ускорения любой задачи; решение зависит от характера обработки и условий выполнения.
Почему нежелательно менять внешнюю переменную внутри map?
Такая зависимость скрывает часть логики за пределами элемента и осложняет понимание порядка выполнения. В нашем примере результат преобразования зависит только от входного числа. Этот подход позволяет проверить каждый этап независимо и легче заметить ошибку.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.