Stream API. Почему не получается вызвать метод .getAsInt() на потоке int[]
Изучаю стримы, задача состоит в том, чтобы из int[] взять положительные числа и сложить их. Не понимаю почему мне выдает ошибку:
error: int cannot be dereferenced
.getAsInt();
Сам код:
import java.util.Arrays;
public class Positive{
public static int sum(int[] arr){
return Arrays.stream(arr)
.filter(x -> x > 0)
.reduce(0, (acc, x) -> acc + x)
.getAsInt();
}
}
Если убрать .getAsInt(), то тесты проходят, но я не очень понимаю почему, ибо в статье, по которой я изучал стримы, был такой пример:
int product = IntStream.range(0, 10)
.filter(x -> x++ % 4 == 0)
.reduce((acc, x) -> acc * x)
.getAsInt();
// product: 0
Да, там стрим примитивов, а у меня элементов массива, но в чем тут принципиальная разница, что там нужно делать так, а здесь — иначе? И что получается, .reduce — это одновременно и терминальный оператор, и промежуточный, если им можно и "закрыть" стрим, так и продолжить после него цепочку вызовов, как в примере из статьи?
Ответы (2 шт):
Итак, по порядку.
но в чем тут принципиальная разница, что там нужно делать так, а здесь — иначе?
Разница в том, какой именно метод reduce() интерфейса Stream используется. Можно заметить, что определено две версии этого метода.
Один метод:
T reduce(T identity, BinaryOperator<T> accumulator);
Второй метод:
Optional<T> reduce(BinaryOperator<T> accumulator);
Правда, в примере из статьи используется интерфейс IntStream, но это неважно, там дела обстоят так же, только специализированно для целых чисел.
Первый вариант метода возвращает сразу же непосредственно то, что вы вычисляете, но за это должен, помимо операции, принимать ещё и дополнительный параметр. В вашем собственном примере вы передаёте 0 для суммы.
Второй вариант метода возвращает Optional. Этот метод уже не имеет необходимой информации в виде дополнительной переменной, и поэтому возвращает Optional - обёртку над вычисляемым значением.
Это просто два разных способа учесть, пустой ли стрим или нет. В случае с дополнительной переменной (ваш пример) если стрим пустой, то вернётся значение того самого дополнительного параметра. В случае без неё (пример из статьи) - будет возвращаться обёртка. Если стрим был пустой, обёртка не будет содержать ничего. Если не был пустым - будет содержать вычисленное значение.
Далее.
И что получается, .reduce — это одновременно и терминальный оператор, и промежуточный...
Нет, это не так. Метод reduce() - исключительно терминальный. Промежуточными являются только те методы, которые возвращают стрим в том или ином виде, но именно стрим. Все остальные методы являются терминальными.
Не понимаю почему мне выдает ошибку:
error: int cannot be dereferenced .getAsInt();
Ошибка сообщает, что у примитивного типа int нельзя вызвать метод getAsInt.
Очевидно, дело в том, что вызов терминального метода IntStream::reduce(int identity, IntBinaryOperator op) возвращает целое число (как и в случае с суммой IntStream::sum), так как здесь уже известен тип параметра identity, в отличие от перегруженного метода IntStream::reduce(IntBinaryOperator op), который и возвращает экземпляр OptionalInt.
Да, там [в статье] стрим примитивов, а у меня элементов массива...
В данном случае метод Arrays.stream для массива примитивов int также возвращает IntStream, а не Stream<Integer>:
public static int sum(int[] arr){
return Arrays.stream(arr) // IntStream
.filter(x -> x > 0) // IntStream
.reduce(0, Integer::sum); // int
}
Stream<Integer> мог бы получиться при обработке коллекции Integer:
public static int sum(Collection<Integer> col) {
return col.stream() // Stream<Integer>
.filter(x -> x > 0) // Stream<Integer>
.reduce(Integer::sum) // Optional<Integer>, а не OptionalInt
.orElse(0); // Integer
// при возвращении значения происходит анбоксинг
}
И что получается,
.reduce— это одновременно и терминальный оператор, и промежуточный, если им можно и "закрыть" стрим, так и продолжить после него цепочку вызовов, как в примере из статьи?
Нет, .reduce -- это терминальная операция, о чём указано в документации по вышеприведённым ссылкам.
Другое дело, что при использовании версии reduce без начального значения, возвращающей OptionalInt для примитивов, можно сразу же вызвать какой-либо метод экземпляра класса OptionalInt, возвращающий целое значение: