Что такое stateful и stateless методы в Java Stream API и какие методы относятся к stateful
Что проверяют
Понимаете ли вы, как устроен конвейер Stream API, и можете ли предсказать поведение при переходе на parallel(). Вопрос отделяет тех, кто читал документацию пакета, от тех, кто выучил список методов.
Как отвечать
Критерий не в том, хранит ли метод что-то между вызовами, а в том, нужно ли операции знать о других элементах, чтобы обработать текущий. Без состояния работают map, filter, flatMap, peek, mapToInt: элемент проходит конвейер независимо от соседей. С состоянием: sorted, distinct, limit, skip.
Внутри этой группы различия существенные. sorted это полный барьер: ни один элемент не уйдёт дальше, пока не прочитан весь источник. distinct помнит уже увиденные элементы, то есть растёт по памяти вместе с их числом и упирается в equals и hashCode. limit и skip последовательно почти бесплатны, но в упорядоченном параллельном потоке дороги: чтобы отдать первые элементы именно по порядку, частям нужно договориться о границах. Снять требование порядка помогает unordered().
Отдельно стоит развести операцию с состоянием и лямбду с состоянием. Лямбда в map, которая пишет во внешнюю переменную, нарушает контракт и ломается на параллельном потоке, даже если сама операция без состояния.
Где спотыкаются
Записывают в stateful терминальные операции вроде collect или, наоборот, относят туда filter из-за состояния внутри лямбды. Промежуточная операция и переданная в неё функция это разные уровни, и на собеседовании их просят различать.
Обещают ускорение от parallel() на конвейере с сортировкой и уникальностью. Там общее состояние и слияние результатов, и параллельная версия нередко проигрывает последовательной, особенно на источнике без известного размера.
Не думают о порядке операций. Фильтр перед сортировкой сокращает объём работы, а limit после sorted уже ничего не спасает: барьер отработал на всём источнике до того, как ограничение вступило в силу.