# Java 流归约

原文：http://zetcode.com/java/streamreduce/

Java 流归约教程展示了如何对 Java8 流执行缩减操作。

## Java 流

Java 流是来自源的支持聚合操作的一系列元素。 流不存储元素。 元素是按需计算的。 元素是从数据源（如集合，数组或 I/O 资源）中消耗的。

## Java 流归约

归约是将流聚合为类或原始类型的终端操作。 Java8 流 API 包含一组预定义的归约操作，例如`average()`，`sum()`，`min()`，`max()`和`count()`，它们通过组合流的元素来返回一个值。

## Java 流`reduce`方法

`Stream.reduce()`是用于生成自定义归约运算的通用方法。

```java
Optional<T> reduce(BinaryOperator<T> accumulator)

```

此方法使用关联累加函数对该流的元素进行归约。 它返回一个`Optional`描述归约的值（如果有）。

```java
T reduce(T identity, BinaryOperator<T> accumulator)

```

此方法采用两个参数：标识和累加器。 如果流中没有元素，则身份元素既是`reduce`的初始值，也是默认结果。 累加器函数具有两个参数：约简的部分结果和流的下一个元素。 它返回一个新的部分结果。 `Stream.reduce()`方法返回归约的结果。

## Java 流内置归约

以下示例使用了两个预定义的归约操作。

`JavaReduceEx.java`

```java
package com.zetcode;

import java.util.Arrays;

public class JavaReduceEx {

    public static void main(String[] args) {

        int vals[] = { 2, 4, 6, 8, 10, 12, 14, 16 };

        int sum = Arrays.stream(vals).sum();
        System.out.printf("The sum of values: %d%n", sum);

        long n = Arrays.stream(vals).count();
        System.out.printf("The number of values: %d%n", n);        
    }
}

```

我们有一个整数数组。 我们使用`Arrays.stream()`从数组创建一个流，并执行两个归约：`sum()`和`count()`。

```java
The sum of values: 72
The number of values: 8

```

这是输出。

## Java `reduce`和`Optional`

具有一个参数的`reduce()`方法返回`Optional`，这是用于`null`安全的 Java 类。

`Car.java`

```java
package com.zetcode;

public class Car {

    private final String name;
    private final int price;

    public Car(String name, int price) {

        this.name = name;
        this.price = price;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Car{name=").append(name).append(", price=")
                .append(price).append("}");

        return builder.toString();
    }
}

```

这是`Car`类。

`JavaReduceEx2.java`

```java
package com.zetcode;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class JavaReduceEx2 {

    public static void main(String[] args) {

        List<Car> persons = Arrays.asList(new Car("Skoda", 18544),
                new Car("Volvo", 22344),
                new Car("Fiat", 23650),
                new Car("Renault", 19700));

        Optional<Car> car = persons.stream().reduce((c1, c2)
                -> c1.getPrice() > c2.getPrice() ? c1 : c2);

        car.ifPresent(System.out::println);
    }
}

```

该示例创建一个汽车对象列表。 我们计算出最昂贵的汽车。

```java
Optional<Car> car = persons.stream().reduce((c1, c2)
            -> c1.getPrice() > c2.getPrice() ? c1 : c2);

```

从列表中，我们创建一个流； `reduce()`方法的累加器会比较汽车的价格并返回价格较高的汽车。

```java
car.ifPresent(System.out::println);

```

如果返回的归约值不为 null，则将其打印到控制台。

```java
Car{name=Fiat, price=23650}

```

这是输出。

下一个示例添加了其他用例。

`MyUtil.java`

```java
package com.zetcode;

public class MyUtil {

    public static int add2Ints(int num1, int num2) {
        return num1 + num2;
    }
}

```

这是`MyUtil`类，具有一种将两个整数相加的方法。

`JavaReduceEx3.java`

```java
package com.zetcode;

import java.util.stream.IntStream;

public class JavaReduceEx3 {

    public static void main(String[] args) {

        IntStream.range(1, 10).reduce((x, y) -> x + y)
                .ifPresent(s -> System.out.println(s));
        IntStream.range(1, 10).reduce(Integer::sum)
                .ifPresent(s -> System.out.println(s));
        IntStream.range(1, 10).reduce(MyUtil::add2Ints)
                .ifPresent(s -> System.out.println(s));
    }
}

```

我们创建三个不同的累加器函数来计算`1..10`值的总和。

```java
IntStream.range(1, 10).reduce((x, y) -> x + y).ifPresent(s -> System.out.println(s));

```

在第一种情况下，我们使用 lambda 表达式进行加法。

```java
IntStream.range(1, 10).reduce(Integer::sum).ifPresent(s -> System.out.println(s));

```

第二种情况使用内置的`Integer::sum`方法。

```java
IntStream.range(1, 10).reduce(MyUtil::add2Ints).ifPresent(s -> System.out.println(s));

```

最后，我们有一个自定义的添加方法。

## Java 归约标识

正如我们已经提到的，如果流中没有元素，则标识既是还原的初始值，又是默认结果。

`User.java`

```java
package com.zetcode;

import java.time.LocalDate;
import java.time.chrono.IsoChronology;

public class User {

    private String name;
    private LocalDate dateOfBirth;

    public User(String name, LocalDate dateOfBirth) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDate getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(LocalDate dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    public int getAge() {
        return dateOfBirth.until(IsoChronology.INSTANCE.dateNow())
                .getYears();
    }

    @Override
    public String toString() {

        StringBuilder builder = new StringBuilder();
        builder.append("User{name=").append(name).append(", dateOfBirth=")
                .append(dateOfBirth).append("}");

        return builder.toString();
    }
}

```

这是`User`类。 除了常用的属性，获取器和设置器之外，我们还有`getAge()`方法，该方法使用 Java8 日期 API 返回用户的年龄。

`JavaReduceEx4.java`

```java
package com.zetcode;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

public class JavaReduceEx4 {

    public static void main(String[] args) {

        List<User> users = new ArrayList<>();
        users.add(new User("Frank", LocalDate.of(1979, 11, 23)));
        users.add(new User("Peter", LocalDate.of(1985, 1, 18)));
        users.add(new User("Lucy", LocalDate.of(2002, 5, 14)));
        users.add(new User("Albert", LocalDate.of(1996, 8, 30)));
        users.add(new User("Frank", LocalDate.of(1967, 10, 6)));

        int maxAge = users.stream().mapToInt(User::getAge)
                .reduce(0, (a1, a2) -> a1 > a2 ? a1 : a2);

        System.out.printf("The oldest user's age: %s%n", maxAge);
    }
}

```

在示例中，我们创建了一个用户列表。 该示例计算最老用户的年龄。

```java
int maxAge = users.stream().mapToInt(User::getAge)
        .reduce(0, (a1, a2) -> a1 > a2 ? a1 : a2);

```

从列表中，我们创建一个 Java8 流。 使用`mapToInt()`方法将流映射到`IntStream`。 最后，`reduce()`方法提供一个标识值（0）和一个累加器； 累加器将比较年龄值并返回较大的值。

在本教程中，我们已经使用 Java 流归约操作。 您可能也对相关教程感兴趣： [Java 流](/lang/java/streams)， [Java 流映射](/java/streammap/)， [Java 流过滤器](/java/streamfilter)和 [Java8 `forEach`教程](/articles/java8foreach/)。