Вызовы потоковых операций Java
Может ли кто-нибудь указать на официальную документацию по Java, в которой описано, сколько раз Stream будет вызывать каждую промежуточную операцию "без вмешательства и без сохранения состояния" для каждого элемента.
Например:
Arrays.asList("1", "2", "3", "4").stream()
.filter(s -> check(s))
.forEach(s -> System.out.println(s));
public boolean check(Object o) {
return true;
}
Вышеупомянутый в настоящее время вызовет метод check
4 раза.
Возможно ли, что в текущих или будущих версиях JDK метод check
выполняется более или менее раз, чем количество элементов в потоке, созданном из List или любого другого стандартного Java API?
Ответы
Ответ 1
Это связано не с источником потока, а с работой терминала и оптимизацией, выполняемой в самой реализации потока. Например:
Stream.of(1,2,3,4)
.map(x -> x + 1)
.count();
Начиная с java-9, map
не будет выполнена ни разу.
Или же:
someTreeSet.stream()
.sorted()
.findFirst();
sorted
может не выполняться вообще, поскольку источником является TreeSet
а получение первого элемента тривиально, но если это реализовано внутри потокового API или нет, это другой вопрос.
Так что реальный ответ здесь - это зависит, но я не могу представить одну операцию, которая выполнялась бы больше, чем количество элементов в источнике.
Ответ 2
Из документации:
Лень-поиск. Многие потоковые операции, такие как фильтрация, отображение или удаление дубликатов, могут быть реализованы лениво, открывая возможности для оптимизации. Например, "найти первую строку с тремя последовательными гласными" не нужно проверять все входные строки. Потоковые операции делятся на промежуточные (генерирующие поток) операции и терминальные (value- или производящие побочный эффект) операции. Промежуточные операции всегда ленивы.
Благодаря этому, поскольку filter
является промежуточной операцией, которая создает новый Stream
как часть своей операции, из-за своей лени он будет вызывать предикат фильтра только один раз для каждого элемента в рамках своей перестройки потока.
Единственный способ, которым ваш метод мог бы иметь другое количество вызовов против него в потоке, - это если бы поток каким-то образом мутировал между состояниями, что с учетом того факта, что ничего в потоке фактически не выполняется до операции терминала, было бы только реально возможным из-за ошибки вверх по течению.