首发于 程序之心

Java 8 | Stream 简介与常用方法

Stream 是什么

在介绍 Stream 之前,先回顾一下以往的集合操作。比如对于一个 list,需要统计其中大于 3 的数字,可能会使用迭代器写出如下的代码遍历 list 进行统计。

List<Integer> list = Arrays.asList(1,2,3,4,5);
Iterator<Integer> iterator = list.iterator();
int countBigThan3 = 0;
while (iterator.hasNext()) {
    Integer value = (Integer) iterator.next();
if(value > 3)
        countBigThan3 ++;
}

这种传统的迭代操作被称为外部迭代,也就是说,迭代的过程在集合之外。外部迭代的过程可以简单表示成下图,迭代中应用代码与集合代码有大量交互,使得应用代码在表达自身逻辑之外还需要大量引用集合代码。

Java 8 为我们带来了 Stream。Stream 是用函数式编程方式在集合类上进行复杂操作的工具。上面的统计代码可以基于 Stream 改写成如下代码。可以看到,代码中几乎只有比较和统计操作,代码简单,逻辑清晰。其中,filter 方法传入了一个 lambda 表达式用于筛选大于 3 的数字,count 方法统计了筛选过数字的个数。

List<Integer> list = Arrays.asList(1,2,3,4,5);
long countBigThan3 = list.stream()
    .filter(value -> value > 3)
    .count();

Stream 的这种迭代操作被称为内部迭代。应用代码通过 Stream 构建迭代的逻辑,然后在集合代码中完成迭代,应用代码只需要迭代后获取结果即可。在上面的代码中,filter 方法描述了 Stream 的操作但并不立即执行,这样的方法被称为惰性求值方法,而 count 方法从 Stream 中获取结果,这样的方法被称为及早求值方法。

collect 方法

collect 方法由 Stream 里的值生成一个列表,是一个及早求值方法。在下面的代码中,使用 of 方法初始化了一个 Stream,然后用 collect 方法生成了一个列表,列表中的元素即是 Stream 中的值。

List<String> lowerList = Stream.of("a","b","c")
    .collect(Collectors.toList());

map 方法

map 方法是一个惰性求值方法,用于把 Stream 中的值替换为新的值。map 方法接受一个 lambda 表达式,而且必须是 Function 接口的实现。Function 接口的参数类型对应旧值的类型,返回值类型对应新值的类型,这两个类型可以是不同的类型。

List<String> upperList = Stream.of("a","b","c")
    .map(value -> value.toUpperCase())
    .collect(Collectors.toList());

filter 方法

filter 方法用于过滤 Stream 中的值,接受一个 lambda 表达式,而且必须是 Predicate 接口的实现。Predicate 接口定义了一个 test 抽象方法,返回 true 的参数会被放入一个新的 Stream 中。

List<Integer> list = Arrays.asList(1,2,3,4,5);
long countBigThan3 = list.stream()
    .filter(value -> value > 3)
    .count();

flatMap 方法

flatMap 可以用于把多个 Stream 连接成一个 Stream。

List<Integer> list = Stream.of(Arrays.asList(1),Arrays.asList(2))
    .flatMap(stream -> stream.stream())
    .collect(Collectors.toList());

max 和 min 方法

max 和 min 方法用于返回 Stream 中的最大值和最小值。这两个方法接受一个 Comparator 对象作为参数,返回一个 Optional 对象。Comparator 接口新增了一个静态的方法 comparing 来方便构建 Comparator 对象,从而可以使用 lambda 表达式来定义最大值和最小值的比较规则。

List<Integer> list = Arrays.asList(1,2,3,4,5);