Какие подводные камни есть у метода count в Stream API в Java
Что проверяют
Знаете ли вы, что count() терминальная операция с неочевидным поведением, и понимаете ли ленивость стримов. Следом обычно дают код с peek() и count() и спрашивают, что он выведет.
Как отвечать
Главный камень появился в Java 9. Реализация вправе не выполнять конвейер вовсе, если размер известен из источника, так прямо сказано в документации count(). Для List.of(1, 2, 3).stream().peek(System.out::println).count() вы получите 3, но ничего не напечатается. То же с map(), он размер не меняет. Стоит добавить filter(), и элементы снова обходятся, потому что размер становится неизвестен. Вывод: побочные эффекты в промежуточных операциях ненадёжны, peek() нужен для отладки, а не для логики.
Остальное:
- бесконечный стрим:
Stream.iterate()илиStream.generate()безlimit()сcount()не завершится никогда; - одноразовость: после
count()стрим закрыт, повторная терминальная операция броситIllegalStateException; - тип
long: результат приводят кintбез проверки и на больших объёмах получают молчаливое переполнение; - проверка на пустоту:
filter(...).count() > 0обходит все элементы, аanyMatch()остановится на первом совпадении; - лишний стрим: у коллекции есть
size(), а подсчёт строк черезFiles.lines()читает весь файл; - память: сам
count()элементы не хранит, ноdistinct()илиsorted()перед ним буферизуют весь поток; - параллельность:
parallel()ради подсчёта на небольшой коллекции только добавит накладных расходов наForkJoinPool.
При группировке для счёта нужен Collectors.counting(), он возвращает Long.
Где спотыкаются
Не знают про пропуск конвейера и уверенно говорят, что peek() напечатает все элементы. Это главный подвох этой темы.
Пишут count() > 0 или count() == 0 там, где нужен короткозамкнутый anyMatch() или noneMatch().
Сохраняют стрим в поле и пытаются использовать повторно, вместо того чтобы хранить коллекцию или Supplier<Stream<T>>.