例子:Stream求和操作
import java.util.stream.*; public class Main { public static void main(String[] args) { int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (acc, n) -> acc + n); System.out.println(sum); // 45 } } BinaryOperator接口对象reduce()方法传入的对象是BinaryOperator接口,它定义了一个apply()方法,负责把上次累加的结果和本次的元素 进行运算,并返回累加的结果:
@FunctionalInterface public interface BinaryOperator<T> { // Bi操作:两个输入,一个输出 T apply(T t, T u); }用for循环改写一下:
Stream<Integer> stream = ... int sum = 0; for (n : stream) { sum = (sum, n) -> sum + n; }例子:用reduce求乘积
import java.util.stream.*; public class Main { public static void main(String[] args) { int s = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(1, (acc, n) -> acc * n); System.out.println(s); // 362880 } } 对Java对象进行操作除了可以对数值进行累积计算外,灵活运用reduce()也可以对Java对象进行操作。下面的代码演示了如何将配置文件的每一行配置通过map()和reduce()操作聚合成一个Map:(后续使用stream的输出为map集合的功能操作起来更加方便)
import java.util.*; public class Main { public static void main(String[] args) { //按行读取配置文件,读取完成后存储在list中 List<String> props = List.of("profile=native", "debug=true", "logging=warn", "interval=500"); //使用Stream的reduce功能 Map<String, String> map = props.stream() //读取到的是每一行的字符 .map(kv -> { //先将字符串拆成等号前后两部分 String[] ss = kv.split("=",2); //每一个(每一行)的字符串都将返回一个map return Map.of(ss[0], ss[1]); }) //将上面得到的多个map聚合成一个map .reduce(new HashMap<String, String>(), (m, kv) -> { m.putAll(kv); return m; }); //打印转化后并且聚合后的map map.forEach((k, v) -> { System.out.println(k + " = " + v); }); } } 小结reduce()方法将一个Stream的每个元素依次作用于BinaryOperator,并将结果合并。
reduce()是聚合方法,聚合方法会立刻对Stream进行计算。
3.5 输出集合对Stream来说操作可以分为两类,
转换操作,即把一个Stream转换为另一个Stream,例如map()和filter(),
聚合操作,即对Stream的每个元素进行计算,得到一个确定的结果,例如reduce()。
区分这两种操作是非常重要的,因为对于Stream来说,对其进行转换操作并不会触发任何计算!
Stream通过collect()方法可以方便地输出为List、Set、Map,还可以分组输出(属于聚合操作)
输出为List如果我们希望把Stream的元素保存到集合,例如List,因为List的元素是确定的Java对象,因此,把Stream变为List不是一个转换操作,而是一个聚合操作,它会强制Stream输出每个元素。
例子: 将一组String先过滤掉空字符串,然后把非空字符串保存到List中
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { Stream<String> stream = Stream.of("Apple", "", null, "Pear", " ", "Orange"); List<String> list = stream.filter(s -> s != null && !s.isBlank()) .collect(Collectors.toList()); System.out.println(list); } }把Stream的每个元素收集到List的方法是调用collect()并传入Collectors.toList()对象,它实际上是一个Collector实例,通过类似reduce()的操作,把每个元素添加到一个收集器中(实际上是ArrayList)。
类似的,collect(Collectors.toSet())可以把Stream的每个元素收集到Set中。
输出为数组把Stream的元素输出为数组和输出为List类似,我们只需要调用toArray()方法,并传入数组的“构造方法”:
List<String> list = List.of("Apple", "Banana", "Orange"); String[] array = list.stream().toArray(String[]::new);注意到传入的“构造方法”是String[]::new,它的签名实际上是IntFunction定义的String[] apply(int),即传入int参数,获得String[]数组的返回值。
输出为Map如果我们要把Stream的元素收集到Map中,就稍微麻烦一点。因为对于每个元素,添加到Map时需要key和value,因此,我们要指定两个映射函数,分别把元素映射为key和value:
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { Stream<String> stream = Stream.of("APPL:Apple", "MSFT:Microsoft"); Map<String, String> map = stream .collect(Collectors.toMap( // 把元素s映射为key: s -> s.substring(0, s.indexOf(\':\')), // 把元素s映射为value: s -> s.substring(s.indexOf(\':\') + 1))); System.out.println(map); } } 分组输出