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

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

Java 流

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

Java 流归约

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

Java 流reduce方法

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

  1. Optional<T> reduce(BinaryOperator<T> accumulator)

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

  1. T reduce(T identity, BinaryOperator<T> accumulator)

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

Java 流内置归约

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

JavaReduceEx.java

  1. package com.zetcode;
  2. import java.util.Arrays;
  3. public class JavaReduceEx {
  4. public static void main(String[] args) {
  5. int vals[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
  6. int sum = Arrays.stream(vals).sum();
  7. System.out.printf("The sum of values: %d%n", sum);
  8. long n = Arrays.stream(vals).count();
  9. System.out.printf("The number of values: %d%n", n);
  10. }
  11. }

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

  1. The sum of values: 72
  2. The number of values: 8

这是输出。

Java reduceOptional

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

Car.java

  1. package com.zetcode;
  2. public class Car {
  3. private final String name;
  4. private final int price;
  5. public Car(String name, int price) {
  6. this.name = name;
  7. this.price = price;
  8. }
  9. public int getPrice() {
  10. return price;
  11. }
  12. @Override
  13. public String toString() {
  14. StringBuilder builder = new StringBuilder();
  15. builder.append("Car{name=").append(name).append(", price=")
  16. .append(price).append("}");
  17. return builder.toString();
  18. }
  19. }

这是Car类。

JavaReduceEx2.java

  1. package com.zetcode;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. import java.util.Optional;
  5. public class JavaReduceEx2 {
  6. public static void main(String[] args) {
  7. List<Car> persons = Arrays.asList(new Car("Skoda", 18544),
  8. new Car("Volvo", 22344),
  9. new Car("Fiat", 23650),
  10. new Car("Renault", 19700));
  11. Optional<Car> car = persons.stream().reduce((c1, c2)
  12. -> c1.getPrice() > c2.getPrice() ? c1 : c2);
  13. car.ifPresent(System.out::println);
  14. }
  15. }

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

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

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

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

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

  1. Car{name=Fiat, price=23650}

这是输出。

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

MyUtil.java

  1. package com.zetcode;
  2. public class MyUtil {
  3. public static int add2Ints(int num1, int num2) {
  4. return num1 + num2;
  5. }
  6. }

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

JavaReduceEx3.java

  1. package com.zetcode;
  2. import java.util.stream.IntStream;
  3. public class JavaReduceEx3 {
  4. public static void main(String[] args) {
  5. IntStream.range(1, 10).reduce((x, y) -> x + y)
  6. .ifPresent(s -> System.out.println(s));
  7. IntStream.range(1, 10).reduce(Integer::sum)
  8. .ifPresent(s -> System.out.println(s));
  9. IntStream.range(1, 10).reduce(MyUtil::add2Ints)
  10. .ifPresent(s -> System.out.println(s));
  11. }
  12. }

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

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

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

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

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

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

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

Java 归约标识

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

User.java

  1. package com.zetcode;
  2. import java.time.LocalDate;
  3. import java.time.chrono.IsoChronology;
  4. public class User {
  5. private String name;
  6. private LocalDate dateOfBirth;
  7. public User(String name, LocalDate dateOfBirth) {
  8. this.name = name;
  9. this.dateOfBirth = dateOfBirth;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public LocalDate getDateOfBirth() {
  18. return dateOfBirth;
  19. }
  20. public void setDateOfBirth(LocalDate dateOfBirth) {
  21. this.dateOfBirth = dateOfBirth;
  22. }
  23. public int getAge() {
  24. return dateOfBirth.until(IsoChronology.INSTANCE.dateNow())
  25. .getYears();
  26. }
  27. @Override
  28. public String toString() {
  29. StringBuilder builder = new StringBuilder();
  30. builder.append("User{name=").append(name).append(", dateOfBirth=")
  31. .append(dateOfBirth).append("}");
  32. return builder.toString();
  33. }
  34. }

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

JavaReduceEx4.java

  1. package com.zetcode;
  2. import java.time.LocalDate;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. public class JavaReduceEx4 {
  6. public static void main(String[] args) {
  7. List<User> users = new ArrayList<>();
  8. users.add(new User("Frank", LocalDate.of(1979, 11, 23)));
  9. users.add(new User("Peter", LocalDate.of(1985, 1, 18)));
  10. users.add(new User("Lucy", LocalDate.of(2002, 5, 14)));
  11. users.add(new User("Albert", LocalDate.of(1996, 8, 30)));
  12. users.add(new User("Frank", LocalDate.of(1967, 10, 6)));
  13. int maxAge = users.stream().mapToInt(User::getAge)
  14. .reduce(0, (a1, a2) -> a1 > a2 ? a1 : a2);
  15. System.out.printf("The oldest user's age: %s%n", maxAge);
  16. }
  17. }

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

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

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

在本教程中,我们已经使用 Java 流归约操作。 您可能也对相关教程感兴趣: Java 流Java 流映射Java 流过滤器Java8 forEach教程