Метод reduce выполняет терминальные операции сведения, возвращая некоторое значение - результат операции. Он имеет следующие формы:
Optional<T> reduce(BinaryOperator<T> accumulator) T reduce(T identity, BinaryOperator<T> accumulator) U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
Первая форма возвращает результат в виде объекта Optional<T>. Например, вычислим произведение набора чисел:
import java.util.stream.Stream;
import java.util.Optional;
public class Program {
public static void main(String[] args) {
Stream<Integer> numbersStream = Stream.of(1,2,3,4,5,6);
Optional<Integer> result = numbersStream.reduce((x,y)->x*y);
System.out.println(result.get()); // 720
}
}
Объект BinaryOperator<T> представляет функцию, которая принимает два элемента и выполняет над ними некоторую операцию, возвращая результат.
При этом метод reduce сохраняет результат и затем опять же применяет к этому результату и следующему элементу в наборе бинарную операцию.
Фактически в данном случае мы получим результат, который будет равен: n1 op n2 op n3 op n4 op n5 op n6, где op - это операция (в данном случае умножения),
а n1, n2, ... - элементы из потока.
Затем с помощью метода get() мы можем получить собственно результат вычислений: result.get()
Или еще один пример - объединение слов в предложение:
Stream<String> wordsStream = Stream.of("мама", "мыла", "раму");
Optional<String> sentence = wordsStream.reduce((x,y)->x + " " + y);
System.out.println(sentence.get());
Вторая версия метода reduce() принимает два параметра:
T reduce(T identity, BinaryOperator<T> accumulator)
Первый параметр - T identity - элемент, который предоставляет начальное значение для функции из второго параметра,
а также предоставляет значение по умолчанию, если поток не имеет элементов.
Второй параметр - BinaryOperator<T> accumulator, как и первая форма метода reduce, представляет ассоциативную функцию, которая запускается
для каждого элемента в потоке и принимает два параметра. Первый параметр
представляяет промежуточный результат функции, а второй параметр - следующий элемент в потоке. Фактически код этого метода будет равноценен следующей записи:
T result = identity; for (T element : this stream) result = accumulator.apply(result, element) return result;
То есть при первом вызове функция accumulator в качестве первого параметра принимает значение identity, а в качестве второго параметра - первый элемент потока. При втором вызове первым параметром служит результат первого вызова функции accumulator, а вторым параметром - второй элемент в потоке и так далее. Например:
Stream<Integer> numberStream = Stream.of(-4, 3, -2, 1); int identity = 1; int result = numberStream.reduce(identity, (x,y)->x * y); System.out.println(result); // 24
Фактически здесь выполняется следующая цепь операций: identity op n1 op n2 op n3 op n4...
В предыдущих примерах тип возвращаемых объектов совпадал с типом элементов, которые входят в поток. Однако это не всегда удобно. Возможно, мы захотим возвратить результат, тип которого отличается от типа объектов потока. Например, пусть у нас есть следующий класс Phone, представляющий телефон:
class Phone{
private String name;
private int price;
public Phone(String name, int price){
this.name=name;
this.price=price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
И мы хотим найти сумму цен тех телефонов, у которых цена меньше определенного значения. Для этого используем третью версию метода reduce:
Stream<Phone> phoneStream = Stream.of(new Phone("iPhone 6 S", 54000),
new Phone("Lumia 950", 45000),
new Phone("Samsung Galaxy S 6", 40000),
new Phone("LG G 4", 32000));
int sum = phoneStream.reduce(0,
(x,y)-> {
if(y.getPrice()<50000)
return x + y.getPrice();
else
return x + 0;
},
(x, y)->x+y);
System.out.println(sum); // 117000
Опять же здесь в качестве первого параметра идет значение по умолчанию - 0. Второй параметр производит бинарную операцию, которая получает промежуточное значение - суммарную цену текущего и предыдущего телефонов. Третий параметр представляет бинарную операцию, которая суммирует все промежуточные вычисления.