Java Stream(流)


Stream是一个数据的序列,支持在这些数据上执行各种聚合操作(Aggregate Operation)。


流分为顺序流并行流两种。

流水线(pipeline)是聚合操作的序列。

流水线包含下面几部分:

  • 流的数据来源,可以为集合、数组等。调用集合的stream方法得到一个Stream。
  • 0或多个中间操作(intermediate operation),中间操作生成一个新的Stream。
  • 一个终止操作(terminal operation),终止操作生成一个非Stream的结果,如一个基本数据类型值或集合或没有值。

下面是Stream的例子:

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

public class StreamCreate {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "orange", "banana");

        list.stream()
            .filter(s -> s.contains("e"))
            .map(String::length)
            .forEach(System.out::println);    // 5 6
    }
}

上面例子中流的数据来源是listfiltermap方法是中间操作,forEach方法是终止操作,没生成值。

reduction操作

reduction操作合并Stream中的所有数据生成一个单值。

下面是reduction操作的例子:

import java.util.stream.IntStream;

public class Reduce {
    public static void main(String[] args) {
        IntStream.of(2, 4, 3)
            .reduce((p1, p2) -> p1 > p2 ? p1 : p2)
                .ifPresent(System.out::println);    // 4

        Integer sum = IntStream.of(1, 2, 3)
                .reduce(0, (a, b) -> a + b);
        System.out.println("sum : " + sum);    // 6
    }
}

上面例子的第二个reduction操作reduce(0, (a, b) -> a + b)0是reduction操作的初始值,如果Stream中没有数据,也是reduction操作的返回值。(a, b) -> a + b是累加器(accumulator)。accumulator的第一个参数a称为部分(partial)结果,即到目前为止Stream中所有处理过的整数的和。第二个参数b是Stream中下一个要处理的数据。a + b返回一个新的partial结果。

Collect操作

collect操作将Stream中的数据转换成另一种类型的值。collect方法的参数Collector包含4个操作:supplier、accumulator、 combiner、finisher。

下面是Collect操作的例子:

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class Collect {
    public static void main(String[] args) {
        List<Pet> pets = Arrays.asList(new Pet("cat", "fish"),
                new Pet("dog", "bone"), new Pet("rabbit", "carrot"));

        Set<Pet> ps = pets.stream()
                .filter(pet -> pet.name.contains("a"))
                .collect(Collectors.toSet());

        System.out.println(ps);    // [rabbit : carrot, cat : fish]

        String foods = pets.parallelStream()
        // String foods = pets.stream()
        .collect(StringBuilder::new,
            (result, pet) -> result.append(" ").append(pet.food),
            (result1, result2) -> result1.append(", ").append(result2))
        .toString();

        System.out.println(foods);    // fish,  bone,  carrot
    }
}

class Pet {
    String name;
    String food;

    public Pet(String name, String food) {
        this.name = name;
        this.food = food;
    }

    public String toString() {
        return name + " : " + food;
    }
}

上面例子中第二个Collect操作中有三个参数:

  • supplier,StringBuilder::new创建一个新的StringBuilder对象,作为结果容器。
  • accumulator,(result, pet) -> result.append(" ").append(pet.food)将Stream中的一个数据pet合并到结果容器result
  • combiner,(result1, result2) -> result1.append(", ").append(result2)合并两个结果容器的内容。如果是并行流(parallel stream)才会执行combiner。

accumulator和combiner不返回值。

map和flatMap操作

map操作:对Stream中的每个数据,应用map方法得到一个结果值,返回一个包含所有结果值的新Stream。

flatMap操作:对Stream中的每个数据,应用map方法得到一个结果Stream,用结果Stream替换原Stream中的对应数据。再flatten所有结果Stream成一个新Stream。

下面是map和flatMap的例子:

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FlatMap {
    public static void main(String[] args) {
        List<Pet> pets = Arrays.asList(new Pet("cat", "fish"),
                new Pet("dog", "bone"), new Pet("rabbit", "carrot"));

        Set<String> ps = pets.stream()
                .map(pet -> pet.name)
                .collect(Collectors.toSet());
        System.out.println(ps);    // [cat, rabbit, dog]

        List<String> list1 = Arrays.asList("red", "orange");
        List<String> list2 = Arrays.asList("green", "blue");
        Stream.of(list1, list2)
                .flatMap(list -> list.stream())
                .forEach(System.out::println);    // red orange green blue
    }
}

class Pet {
    String name;
    String food;

    public Pet(String name, String food) {
        this.name = name;
        this.food = food;
    }

    public String toString() {
        return name + " : " + food;
    }
}

上面例子中执行flatMap(list -> list.stream())时,Stream中数据list1经过map后生成一个流Stream1,Stream中数据list2经过map后生成一个流Stream2,最后合并Stream1中的数据和Stream2中的数据生成一个新的Stream。