1. 前言

Java8提供了Stream操作,针对集合可以完成复杂的查找、过滤以及数据映射等操作。

完成一个完整的Stream操作,可分为三步走:

(1) 流的创建:从数据源(集合或数组)中获取一个流;

(2) 中间操作:对流进行过滤、筛选等操作;

(3) 终止操作:对流进行最后的统计或收集等操作。

注意:中间操作需要等到终止操作调用才会执行。

2. 流的创建

2.1 Collection

Collection接口提供两种方法来创建流。

  • Stream<E> stream() 返回一个顺序流;
  • Stream<E> parallelStream() 返回一个并行流。

注意:数据量不大的情况下,使用顺序流便足够了。

栗子:

static void testParallel() {
    // 程序开始时间
    Instant startTime = Instant.now();

    List<String> list = Arrays.asList("java", "python", "javascript", "html");

    //顺序流
    Stream<String> stream = list.stream().filter(i -> i.startsWith("ja"));
    stream.forEach(System.out::println);

    Instant middleTime = Instant.now();

    //并行流
    Stream<String> stream2 = list.parallelStream().filter(i -> i.startsWith("ja"));
    stream2.forEach(System.out::println);

    // 程序结束时间
    Instant endTime = Instant.now();
    long millis1 = ChronoUnit.MILLIS.between(startTime, middleTime);
    long millis2 = ChronoUnit.MILLIS.between(middleTime, endTime);

    System.out.println("stream程序消耗时间(毫秒)" + millis1);
    System.out.println("parallelStream程序消耗时间(毫秒)" + millis2);
}

输出结果:

javascript
java
javascript
java
stream程序消耗时间(毫秒)73
parallelStream程序消耗时间(毫秒)82
2.2 Arrays

数组可以通过Arrays.stream(arr)创建一个流。

栗子:

static void testArrayStream(){
    String[] arr = {"java", "python", "javascript", "html"};
    Stream<String> stream = Arrays.stream(arr).filter(i -> i.startsWith("ja"));
    stream.forEach(System.out::println);
}
2.3 Stream

通过Stream.of()可以直接创建一个流。

栗子:

static void testStreamDirect() {
    Stream<String> stream = Stream.of("java", "python", "javascript", "html") //根据多个Value值直接创建一个流
            .filter(i -> i.startsWith("ja")); //半路拦截,只留下以“ja”开头的字符串
    stream.forEach(System.out::println);
}

通过Stream.iterate()Stream.generate()创建无限流。

栗子:

static void testInfiniteStream(){
    //Stream.iterate()
    Stream<Integer> stream = Stream.iterate(10, x -> x * 2); //从10开始,每次翻倍
    stream.limit(3).forEach(System.out::println);
    System.out.println("----------- 我是最靓分割线 ------------");
    //Stream.generate()
    Stream.generate(Math::random).limit(5).forEach(System.out::println); //整5个随机数玩玩
}

输出结果:

10
20
40
----------- 我是最靓分割线 ------------
0.28733224175718597
0.37745872228126487
0.5575898714168762
0.8904672894488914
0.27626278982879915

3. 中间操作

3.1 筛选
3.1.1 filter

对流中的元素进行过滤,满足条件的留下,不满足的全部Game Over

栗子:

static void testFilter(){
    List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
    list.stream().filter(i -> i % 2 == 0) // 偶数者留,奇数者去也
            .forEach(System.out::println);
}

// result: 4 2 10
3.1.2 limit

限流,只允许指定个数的元素通过,后面的元素一边凉快去吧。

栗子:

static void testLimit(){
    List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
    list.stream().limit(3) // 只保留前3个元素
            .forEach(System.out::println);
}

// result: 1 3 4
3.1.3 skip

跳过,抛弃前面的指定个数的元素,直接跳跃到后面进行流处理。

栗子:

static void testSkip(){
    List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
    list.stream().skip(3) // 只保留前3个元素
            .forEach(System.out::println);
}

// result: 2 7 10
3.1.4 distinct

去重,过滤重复的元素,相同元素只保留一个。

栗子:

static void testDistinct(){
    List<String> list = Arrays.asList("java","java","java","javascript");
    list.stream().distinct() // 请尝尝传说中的归一大法吧
            .forEach(System.out::println);
}

// result: java javascript
3.2 排序
3.2.1 默认排序

栗子:

static void testSorted(){
    List<String> list = Arrays.asList("short","int","char","byte");
    list.stream().sorted() //自然排序
            .forEach(System.out::println);
}

// result: byte char int short
3.2.2 自定义排序

栗子:

static void testDefinedSorted(){
    List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
    list.stream().sorted((a, b) -> b - a)   //倒序
            .forEach(System.out::println);
}

// result: 10 7 4 3 2 1
3.3 映射
3.3.1 map

将流中的元素的值经过一番变化映射成另外一个值。
栗子:

static void testSimpleMap(){
    List<Integer> list = Arrays.asList(1, 2, 3, 4);
    list.stream().map(i -> i * 10)
            .forEach(System.out::println);
}

//result: 10 20 30 40
3.3.2 flatMap

mapflatMap有点类似于List中的addaddAll

add添加一个List只会当做原来的List中一个普通成员,而如果是addAll,则会将新添加的List和原来的List合并成一个List

static void testAddAll(){
    List<Object> oldList = new ArrayList<>();
    oldList.add(1); //添加单个元素
    oldList.add(Arrays.asList(2, 3)); //添加 List
    System.out.println(Arrays.toString(oldList.toArray()));

    oldList.clear(); //清空
    oldList.add(1); //添加单个元素
    oldList.addAll(Arrays.asList(2, 3)); //添加 List
    System.out.println(Arrays.toString(oldList.toArray()));
}

// result:
// [1, [2, 3]]      //纵横交错深万里
// [1, 2, 3]        //水平扩展一线牵

map处理中如果返回的是Stream,那么只会在原来的Stream中添加Stream作为普通成员,最终的结果是Stream中包含Stream

而如果flapMap处理中如果返回的是Stream,则会将其合并到原来的Stream中去,最终的结果只有一个Stream

栗子:

//比 FlatMap 多了一层 foreach
static void testMap(){
    List<Integer[]> list = Arrays.asList(new Integer[]{1 , 2}, new Integer[]{3, 4});
    list.stream().map(integers -> Arrays.stream(integers).map(i -> i * 10))
            .forEach(integerStream -> {integerStream.forEach(System.out::println);});
}

//result: 10 20 30 40

static void testFlatMap(){
    List<Integer[]> list = Arrays.asList(new Integer[]{1 , 2}, new Integer[]{3, 4});
    list.stream().flatMap(integers -> Arrays.stream(integers).map(i -> i * 10))
            .forEach(System.out::println);
}

//result: 10 20 30 40

4. 终止操作

看来是藏不住杀招了,上面的中间操作方法返回类型都Stream,下面的终止操作返回类型都非Stream

4.1 匹配
4.1.1 anyMatch

只要任意一个满足匹配条件,便返回true

static void testAnyMatch(){
    List<String> list = Arrays.asList("java", "javascript", "python", "html");
    boolean b = list.stream().anyMatch(i -> i.startsWith("p"));
    System.out.println("所有元素字符串中,只要有一个元素是以p开头,便会返回" + b);
}

// result: 所有元素字符串中,只要有一个元素是以p开头,便会返回true
4.1.2 allMatch

所有元素满足匹配条件,才会返回true

栗子:

static void testAllMatch(){
    List<String> list = Arrays.asList("java", "javascript", "python", "html");
    boolean b = list.stream().skip(1) //跳过不包含 t 的 java
            .allMatch(i -> i.contains("t"));
    System.out.println("需要全部元素都包含t,便会返回" + b);
}

// result: 需要全部元素都包含t,便会返回true
4.1.3 noneMatch

没有一个元素能够满足匹配条件,便会返回true

栗子:

static void testNoneMatch(){
    List<String> list = Arrays.asList("java", "javascript", "python", "html");
    boolean b = list.stream().noneMatch(i -> i.contains("z"));
    System.out.println("没有一个元素都包含z,便会返回" + b);
}

// result: 没有一个元素都包含z,便会返回true
4.2 查找
4.2.1 findFirst

返回流中的第一个元素。

栗子:

static void testFindFirst(){
    List<String> list = Arrays.asList("java", "javascript", "python", "html");
    Optional<String> s = list.stream().filter(i -> i.contains("t"))
            .findFirst();
    System.out.println("返回第一个元素:" + s);
}

// result: 返回第一个元素:Optional[javascript]
4.2.2 findAny

返回流中任意一个元素。

栗子:

static void testFindAny(){
    List<String> list = Arrays.asList("java", "javascript", "python", "html");
    Optional<String> s = list.stream().filter(i -> i.contains("a"))
            .findAny();
    System.out.println("返回任意一个元素:" + s);
}

// result: 返回任意一个元素:Optional[java]
4.3 统计
4.3.1 `count
static void testCount(){
    List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
    long count = list.stream().filter(i -> i % 2 == 0).count();
    System.out.println("集合中偶数的个数:" + count);
}

// result: 集合中偶数的个数:3
4.3.2 max
static void testMax(){
    List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
    Optional<Integer> integer = list.stream().filter(i -> i % 2 == 0)
            .max(Integer::compareTo);
    System.out.println("集合中偶数的最大值:" + (integer.orElse(null)));
}

// result: 集合中偶数的最大值:10
4.3.3 min
static void testMin(){
    List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
    Optional<Integer> integer = list.stream().filter(i -> i % 2 == 0)
            .min(Integer::compareTo);
    System.out.println("集合中偶数的最小值:" + (integer.orElse(null)));
}

// result: 集合中偶数的最小值:2
4.4 归约
static void testReduce(){
    List<Integer> list = Arrays.asList(1, 3, 4);
    Integer integer = list.stream().reduce(100, (a, b) -> a + b * 2);
    System.out.println("初始值为100,然后将集合中的数值乘以2,再与初始值累加。");
    System.out.println("最终结果:100 + (1 * 2) + (3 * 2) + (4 * 2) = " + integer);
}
// result:
// 初始值为100,然后将集合中的数值乘以2,再与初始值累加。
// 最终结果:100 + (1 * 2) + (3 * 2) + (4 * 2) = 116
4.5 收集

主要介绍Collectors工具类的使用。

4.5.1 返回Collection

将流中的元素装入List集合。

List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toList());

将流中的元素装入Set集合。

Set<Integer> set = Stream.of(1, 2, 3).collect(Collectors.toSet());

将流中元素装入Map集合。

// key: x , value: x * x
Map<Integer, Integer> map = Stream.of(1, 2, 3).collect(Collectors.toMap(x -> x, x -> x * x));
Optional<String> s = map.entrySet().stream()
        .map(x ->  "{" + x.getKey() + ":" + x.getValue() + "}")
        .reduce((x, y) -> x + "," + y);
System.out.println("打印Map新方式,result = [" + s.orElse(null) + "]");

// result: 打印Map新方式,result = [{1:1},{2:4},{3:9}]

将流中的元素装入指定集合。

HashSet<Integer> hashSet = Stream.of(1, 2, 3).collect(Collectors.toCollection(HashSet::new));
4.5.2 拼接字符串
static void testJoinStr(){
    List<String> list = Arrays.asList("java","javascript");
    String collect1 = list.stream().collect(Collectors.joining());
    String collect2 = list.stream().collect(Collectors.joining(","));
    String collect3 = list.stream().collect(Collectors.joining(",", "{", "}"));
    System.out.println(collect1);
    System.out.println(collect2);
    System.out.println(collect3);
}

// result:
// javajavascript
// java,javascript
// {java,javascript}
4.5.2 统计

统计流中元素的个数

Long count = Stream.of(1, 2, 3).collect(Collectors.counting());

统计流中元素的最大值

Optional<Integer> max = Stream.of(1, 2, 3).collect(Collectors.maxBy(Integer::compareTo));

统计流中元素的最小值

Optional<Integer> min = Stream.of(1, 2, 3).collect(Collectors.minBy(Integer::compareTo));

统计流中元素的平均值

// 除了 averagingInt ,还有 averagingLong , averagingDouble
Double avg = Stream.of(1, 1, 2, 3).collect(Collectors.averagingInt(x -> x));

统计流中元素的总和

// 除了 summingInt ,还有 summingLong , summingDouble
Integer sum = Stream.of(1, 1, 2, 3).collect(Collectors.summingInt(x -> x));

统计汇总

// 除了 summarizingInt ,还有 summarizingLong , summarizingDouble
IntSummaryStatistics statistics = Stream.of(1, 1, 2, 3).collect(Collectors.summarizingInt(x -> x));
long count1 = statistics.getCount();
int max1 = statistics.getMax();
int min1 = statistics.getMin();
double average = statistics.getAverage();
long sum1 = statistics.getSum();
4.5.3 其他用法

Collectors.reducingStream.reduce类似,进行累计运算。

// 100 + ((1 + 2) + 3)
Integer collect = Stream.of(1, 2, 3).collect(Collectors.reducing(100, (x, y) -> x + y));

// 100 + (((1 + 1) + (2 + 1)) + (3 + 1))
Integer collect = Stream.of(1, 2, 3).collect(Collectors.reducing(100, x -> x + 1, (x, y) -> x + y));

Collectors.mappingStream.map类似,进行数据映射。

Integer integer = Stream.of(1, 2, 3).collect(Collectors.mapping(x -> x * 2, Collectors.summingInt(x -> x)));

String collect = Stream.of("a", "b", "c").collect(Collectors.mapping(x -> x.toUpperCase(), Collectors.joining(",")));

Collectors.collectingAndThen

String s = Stream.of("a", "b", "c").collect(Collectors.collectingAndThen(Collectors.joining(","), x -> x.toUpperCase()));

// result: A,B,C