Почему неправильный порядок вызова limit и sort вызывает ошибку на бесконечной последовательности в Java Stream
Что проверяют
Понимаете ли вы, какие операции конвейера могут завершиться на бесконечном источнике, а какие обязаны прочитать его целиком. Ленивость стримов часто понимают как обещание, что лишней работы не будет никогда, и вопрос ровно про это заблуждение.
Как отвечать
Промежуточные операции делятся на операции без состояния (map, filter), операции с состоянием (sorted, distinct) и короткозамыкающие (limit, takeWhile). sorted это полный барьер: чтобы отдать первый элемент, он должен получить и разложить в памяти все элементы источника.
На бесконечном источнике вроде Stream.iterate это значит, что программа не падает с понятным сообщением, а работает бесконечно и в итоге упирается в память. Поэтому ограничение ставится до сортировки:
Stream.iterate(0, n -> n + 1)
.limit(10)
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);Важная оговорка, которую и ждут на уточняющем вопросе: это не универсальное правило «limit всегда раньше». На конечном источнике sorted().limit(n) и limit(n).sorted() означают разное. Первое это выбор лучших n по всей выборке, второе сортировка произвольных первых n. Переставлять их местами ради скорости нельзя: конвейер стрима не переупорядочивается сам, в отличие от плана запроса в базе. Для бесконечного источника задача выбора лучших n просто не имеет смысла, область сначала надо ограничить через limit, takeWhile или вариант Stream.iterate с условием продолжения.
Где спотыкаются
Говорят, что сортировка бесконечного стрима выбросит исключение. Нет, она зависнет, и это хуже: в продакшене такое выглядит как намертво занятый поток без единой строчки в логе.
Заучивают порядок как правило и переносят limit вперёд в обычном конечном коде. Выборка лучших записей превращается в сортировку случайной десятки, тесты на маленьких данных этого не ловят.
Считают, что ленивость избавляет от полного прохода. Ленивость означает лишь, что до терминальной операции ничего не выполняется. Она ничего не обещает про sorted и distinct, которым по природе нужны все элементы или память под уже увиденные.