Java 流归约
原文:http://zetcode.com/java/streamreduce/
Java 流归约教程展示了如何对 Java8 流执行缩减操作。
Java 流
Java 流是来自源的支持聚合操作的一系列元素。 流不存储元素。 元素是按需计算的。 元素是从数据源(如集合,数组或 I/O 资源)中消耗的。
Java 流归约
归约是将流聚合为类或原始类型的终端操作。 Java8 流 API 包含一组预定义的归约操作,例如average()
,sum()
,min()
,max()
和count()
,它们通过组合流的元素来返回一个值。
Java 流reduce
方法
Stream.reduce()
是用于生成自定义归约运算的通用方法。
Optional<T> reduce(BinaryOperator<T> accumulator)
此方法使用关联累加函数对该流的元素进行归约。 它返回一个Optional
描述归约的值(如果有)。
T reduce(T identity, BinaryOperator<T> accumulator)
此方法采用两个参数:标识和累加器。 如果流中没有元素,则身份元素既是reduce
的初始值,也是默认结果。 累加器函数具有两个参数:约简的部分结果和流的下一个元素。 它返回一个新的部分结果。 Stream.reduce()
方法返回归约的结果。
Java 流内置归约
以下示例使用了两个预定义的归约操作。
JavaReduceEx.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()
。
The sum of values: 72
The number of values: 8
这是输出。
Java reduce
和Optional
具有一个参数的reduce()
方法返回Optional
,这是用于null
安全的 Java 类。
Car.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
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);
}
}
该示例创建一个汽车对象列表。 我们计算出最昂贵的汽车。
Optional<Car> car = persons.stream().reduce((c1, c2)
-> c1.getPrice() > c2.getPrice() ? c1 : c2);
从列表中,我们创建一个流; reduce()
方法的累加器会比较汽车的价格并返回价格较高的汽车。
car.ifPresent(System.out::println);
如果返回的归约值不为 null,则将其打印到控制台。
Car{name=Fiat, price=23650}
这是输出。
下一个示例添加了其他用例。
MyUtil.java
package com.zetcode;
public class MyUtil {
public static int add2Ints(int num1, int num2) {
return num1 + num2;
}
}
这是MyUtil
类,具有一种将两个整数相加的方法。
JavaReduceEx3.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
值的总和。
IntStream.range(1, 10).reduce((x, y) -> x + y).ifPresent(s -> System.out.println(s));
在第一种情况下,我们使用 lambda 表达式进行加法。
IntStream.range(1, 10).reduce(Integer::sum).ifPresent(s -> System.out.println(s));
第二种情况使用内置的Integer::sum
方法。
IntStream.range(1, 10).reduce(MyUtil::add2Ints).ifPresent(s -> System.out.println(s));
最后,我们有一个自定义的添加方法。
Java 归约标识
正如我们已经提到的,如果流中没有元素,则标识既是还原的初始值,又是默认结果。
User.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
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);
}
}
在示例中,我们创建了一个用户列表。 该示例计算最老用户的年龄。
int maxAge = users.stream().mapToInt(User::getAge)
.reduce(0, (a1, a2) -> a1 > a2 ? a1 : a2);
从列表中,我们创建一个 Java8 流。 使用mapToInt()
方法将流映射到IntStream
。 最后,reduce()
方法提供一个标识值(0)和一个累加器; 累加器将比较年龄值并返回较大的值。
在本教程中,我们已经使用 Java 流归约操作。 您可能也对相关教程感兴趣: Java 流, Java 流映射, Java 流过滤器和 Java8 forEach
教程。