Java stream流式计算详解

Java stream流式计算详解

  • 1. Stream概述
    • 1.1 Stream简介
    • 1.2 Stream分类
  • 2. Stream操作
    • 2.1 Stream创建
    • 2.2 Stream无状态操作
    • 2.3 Stream有状态操作
    • 2.4 Stream短路操作
    • 2.5 Stream非短路操作
  • 3. 测试验证

1. Stream概述

1.1 Stream简介

对于Java8提供的Stream与Lambda表达式结合使用的功能,每个人看法不一,这种语法糖操作确实带来了一定的便利,使代码更加的简洁,但同时也在降低代码的可读性,代码是给人读的不只是机器运行的,所以具体使用看个人的接受程度以及书写风格了。

1.2 Stream分类

Stream 提供的方法非常多,按照调用当前方法是否结束流处理,可以分为中间操作和结束操作。

对于中间操作,又可以分为有状态的操作和无状态操作:
1.无状态的操作是指当前元素的操作不受前面元素的影响。
2.有状态的操作是指当前元素的操作需要等所有元素处理完之后才能进行。

对于结束操作,又可以分为短路操作和非短路操作,具体如下:
1.短路操作是指不需要处理完全部的元素就可以结束。
2.非短路操作是指必须处理完所有元素才能结束。

Stream无状态操作:map,mapToXXX,flatMap,flatMapToXXX,filter,peek,unordered
Stream有状态操作:distinct,limit,skip,sorted
Stream短路操作:findAny,anyMatch,allMatch,noneMatch,findFirst
Stream非短路操作:forEach,forEachOrdered,toArray,reduce,collect,max、min、count

2. Stream操作

2.1 Stream创建

Stream创建:使用集合创建,使用数组创建,使用 Stream 静态方法

    /**
     * 1.创建Stream
     * 1.1 使用集合创建
     * 1.2 使用数组创建
     * 1.3 使用 Stream 静态方法
     */
    @Test
    public void createStream() {
        //1.1使用集合创建
        List<Integer> list = Arrays.asList(5, 2, 3, 1, 4);
        Stream streamInt = list.stream();
        printStream(streamInt, "1.1使用集合创建Stream:");
        System.out.println("-----------------------");

        //1.2使用数组创建
        String[] array = {"ab", "abc", "abcd", "abcde", "abcdef"};
        Stream<String> streamStr = Arrays.stream(array);
        printStream(streamStr, "1.2使用数组创建Stream:");
        System.out.println("-----------------------");

        //1.3使用 Stream 静态方法
        Stream<String> streamStatic = Stream.of("ab", "abc", "abcd", "abcde", "abcdef");
        printStream(streamStatic, "1.3使用 Stream 静态方法Stream:");
        System.out.println("-----------------------");

        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 3).limit(5);
        printStream(stream4, "Stream迭代器:");

        Stream<Integer> stream5 = Stream.generate(new Random()::nextInt).limit(3);
        printStream(stream5, "Stream构建器:");
        System.out.println("-----------------------");

    }

2.2 Stream无状态操作

Stream无状态操作:map,mapToXXX,flatMap,flatMapToXXX,filter,peek,unordered


    /**
     * 2.Stream无状态操作
     * 2.1 map
     * 2.2 mapToXXX
     * 2.3 flatMap
     * 2.4 flatMapToXXX
     * 2.5 filter
     * 2.6 peek
     * 2.7 unordered
     */
    @Test
    public void operationStreamStateless() {
        //2.1 map,接收一个函数作为入参,把这个函数应用到每个元素上,执行结果组成一个新的 stream 返回。
        //案例1:对整数数组每个元素加3 :
        List<Integer> list = Arrays.asList(5, 2, 3, 1, 4);
        List<Integer> newList = list.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println("案例1:对整数数组每个元素加3的值:" + newList);

        //案例2:把字符串数组的每个元素转换为大写:
        List<String> listStr = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef");
        List<String> newListStr = listStr.stream().map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("案例2:把字符串数组的每个元素转换为大写:" + newListStr);
        System.out.println("-----------------------");

        //2.2 mapToXXX,包括三个方法:mapToInt、mapToDouble、mapToLong
        //案例3:把字符串数组转为整数数组:
        List<String> listStr3 = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef");
        int[] newList3 = listStr3.stream().mapToInt(r -> r.length()).toArray();
        System.out.println("案例3:把字符串数组转为整数数组: " + Arrays.toString(newList3));
        System.out.println("-----------------------");

        //2.3 flatMap,flatMap接收函数作为入参,然后把集合中每个元素转换成一个stream,再把这些stream组成一个新的stream,是拆分单词很好的工具。
        //案例4:把一个字符串数组转成另一个字符串数组:
        List<String> list4 = Arrays.asList("ab-abc-abcd-abcde-abcdef", "5-2-3-1-4");
        List<String> newList4 = list4.stream().flatMap(s -> Arrays.stream(s.split("-"))).collect(Collectors.toList());
        System.out.println("案例4:把一个字符串数组转成另一个字符串数组 " + newList4);
        System.out.println("-----------------------");

        //2.4 flatMapToXXX,类似于flatMap,返回一个XXXStream。包括三个方法:flatMapToInt、flatMapToLong、flatMapToDouble
        //案例5:对给定的二维整数数组求和:
        int[][] data = {{1, 2}, {3, 4}, {5, 6}};
        IntStream intStream = Arrays.stream(data).flatMapToInt(row -> Arrays.stream(row));
        System.out.println("案例5:对给定的二维整数数组求和:" + intStream.sum());
        System.out.println("-----------------------");

        //2.5 filter,筛选功能,按照一定的规则将符合条件的元素提取到新的流中。
        //案例6:找出考试成绩在 90 分以上的学生姓名:
        List<Student> students = buildStudentList();
        List<String> nameList = students.stream().filter(x -> x.getScore() >= 90).map(Student::getName).collect(Collectors.toList());
        System.out.println("案例6:找出考试成绩在90分以上的学生姓名:" + nameList);
        System.out.println("-----------------------");

        //2.6 peek,
        //返回由 stream 中元素组成的新 stream,用给定的函数作用在新 stream 的每个元素上。
        //传入的函数是一个 Consume 类型的,没有返回值,因此并不会改变原 stream 中元素的值。peek 主要用是 debug,可以方便地 查看流处理结果是否正确。

        //案例7:过滤出 stream 中长度大于 3 的字符串并转为大写:
        List<String> collect = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.print("," + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("->" + e))
                .collect(Collectors.toList());
        System.out.println("案例7:过滤出stream中长度大于3的字符串并转为大写:" + collect);
        System.out.println("-----------------------");

        //2.7 unordered.
        //把一个有序的 stream 转成一个无序 stream ,如果原 stream 本身就是无序的,可能会返回原始的 stream。
        //案例8:把一个有序数组转成无序,每次执行输出的结果不一样。
        System.out.print("案例8:把一个有序数组转成无序:");
        Arrays.asList("1", "2", "3", "4", "5")
                .parallelStream()
                .unordered()
                .forEach(r -> System.out.print(r + " "));
        System.out.println("\n-----------------------");

    }

2.3 Stream有状态操作

Stream有状态操作:distinct,limit,skip,sorted


    /**
     * 3.Stream有状态操作
     * 3.1 distinct
     * 3.2 limit
     * 3.3 skip
     * 3.4 sorted
     */
    @Test
    public void operationStreamState() {
        //3.1 distinct,去重功能。
        //案例9 :去掉字符串数组中的重复字符串
        String[] array = {"a", "b", "b", "c", "c", "d", "d", "e", "e"};
        List<String> newList = Arrays.stream(array).distinct().collect(Collectors.toList());
        System.out.println("案例9 :去掉字符串数组中的重复字符串:" + newList);
        System.out.println("-----------------------");

        //3.2 limit,限制从 stream 中获取前 n 个元素。
        //案例10 :从数组中获取前 5 个元素
        String[] array10 = {"c", "c", "a", "b", "b", "e", "e", "d", "d"};
        List<String> newList10 = Arrays.stream(array10).limit(5).collect(Collectors.toList());
        System.out.println("案例10 :从数组中获取前 5 个元素:" + newList10);
        System.out.println("-----------------------");

        //3.3 skip,跳过 Stream 中前 n 个元素
        //案例11:从数组中获取第5个元素之后的元素
        String[] array11 = {"a", "b", "c", "d", "e", "f", "g", "h", "i"};
        List<String> newList11 = Arrays.stream(array11).skip(5).collect(Collectors.toList());
        System.out.println("案例11:从数组中获取前5个元素之后的元素:" + newList11);
        System.out.println("-----------------------");

        //3.4 sorted,排序功能。
        //案例12:对给定数组进行排序
        String[] array12 = {"c", "c", "a", "b", "b", "e", "e", "d", "d"};
        List<String> newList12 = Arrays.stream(array12).sorted().collect(Collectors.toList());
        System.out.println("案例12:对给定数组进行排序:" + newList12);
        Integer[] array121 = {3, 6, 5, 4, 1, 2, 2, 3, 6};
        List<Integer> newList121 = Arrays.stream(array121).sorted().collect(Collectors.toList());
        System.out.println("案例12:对给定数组进行排序:" + newList121);
        System.out.println("-----------------------");

        //案例13:按照学生成绩进行排序,正序。
        List<Student> students = buildStudentList();
        List<String> nameList = students.stream().sorted(Comparator.comparing(Student::getScore)).map(Student::getName).collect(Collectors.toList());
        System.out.println("案例13:按照学生成绩进行排序:" + nameList);
        System.out.println("-----------------------");
    }

2.4 Stream短路操作

Stream短路操作:findAny,anyMatch,allMatch,noneMatch,findFirst

    /**
     * 4.Stream短路操作
     * 4.1 findAny
     * 4.2 anyMatch
     * 4.3 allMatch
     * 4.4 noneMatch
     * 4.5 findFirst
     */
    @Test
    public void operationStreamShortCircuit() {

        //4.1 findAny,找出 stream 中任何一个满足过滤条件的元素。
        //案例14:找出任何一个成绩高于 90 分的学生。排完序取得第一个,最高分。
        List<Student> students = buildStudentList();
        Optional<Student> studentFindAny = students.stream().filter(x -> x.getScore() > 90).findAny();
        System.out.println("案例14:找出任意一个考试成绩在90分以上的学生姓名:" + studentFindAny.orElseGet(null).getName());
        System.out.println("-----------------------");

        //4.2 anyMatch,是否存在任意一个满足给定条件的元素。
        //案例15:是否存在成绩高于 90 分的学生,是否存在成绩低于 50 分的学生。
        boolean result1 = students.stream().anyMatch(x -> x.getScore() > 90);
        System.out.println("案例15:是否存在成绩高于 90 分的学生:" + result1);
        boolean result2 = students.stream().anyMatch(x -> x.getScore() < 50);
        System.out.println("案例15:是否存在成绩低于 50 分的学生:" + result2);
        System.out.println("-----------------------");

        //4.3 allMatch,是否集合中所有元素都满足给定的条件,如果集合是空,则返回 true。
        //案例16:学生成绩是否都高于 90 分,是否都高于 50 分。
        boolean result16 = students.stream().allMatch(x -> x.getScore() > 90);
        System.out.println("案例16:是否所有学生的成绩都高于90分:" + result16);
        boolean result26 = students.stream().allMatch(x -> x.getScore() > 50);
        System.out.println("案例16:是否所有学生的成绩都高于50分:" + result26);
        System.out.println("-----------------------");

        //4.4 noneMatch,是否没有元素能匹配给定的条件,如果集合是空,则返回 true。
        //案例17:是不是没有学生成绩在 90 分以上,是否没有学生成绩在 50 分以下。
        boolean result17 = students.stream().noneMatch(x -> x.getScore() > 90);
        System.out.println("案例17:是不是没有学生成绩在 90 分以上:" + result17);
        boolean result27 = students.stream().noneMatch(x -> x.getScore() < 50);
        System.out.println("案例17:是不是没有学生成绩在 50 分以下:" + result27);
        System.out.println("-----------------------");

        //4.5 findFirst,找出第一个符合条件的元素。
        //案例18:找出第一个成绩在 90 分以上的学生。
        Optional<Student> studentFindFirst = students.stream().filter(x -> x.getScore() > 90).findFirst();
        System.out.println("案例18:第一个成绩在 90 分以上的学生姓名:" + studentFindFirst.orElseGet(null).getName());
        System.out.println("-----------------------");

    }

2.5 Stream非短路操作

Stream非短路操作:forEach,forEachOrdered,toArray,reduce,collect,max、min、count


    /**
     * 5.Stream非短路操作
     * 5.1 forEach
     * 5.2 forEachOrdered
     * 5.3 toArray
     * 5.4 reduce
     * 5.5 collect
     * 5.6 max、min、count
     */
    @Test
    public void operationStreamNonShortCircuit() {
        //5.1 forEach,遍历元素。
        //案例19:遍历一个数组并打印
        List<Integer> array = Arrays.asList(5, 2, 3, 1, 4);
        System.out.print("案例19:遍历一个数组并打印:");
        array.stream().forEach(System.out::print);
        System.out.println("\n-----------------------");

        //5.2 forEachOrdered,按照给定集合中元素的顺序输出。主要使用场景是在并行流的情况下,按照给定的顺序输出元素。
        //案例20:用并行流遍历一个数组并按照给定数组的顺序输出结果
        System.out.print("案例20:用并行流遍历一个数组并按照给定数组的顺序输出结果:");
        array.parallelStream().forEachOrdered(System.out::print);
        System.out.println("\n-----------------------");

        //5.3 toArray,返回包括给定 stream 中所有元素的数组。
        //案例21:把给定字符串流转化成数组
        Stream<String> stream = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef").stream();
        String[] newArray1 = stream.toArray(str -> new String[5]);
        //String[] newArray2 = stream.toArray(String[]::new);
        //Object[] newArray3 = stream.toArray();
        //注意:stream流只能使用一次就好自动关闭,不能转化多次,所以newArray1,newArray2,newArray3不能同时放开
        //如果要使用多次就要使用复制功能,把stream复制为输入流多次拷贝
        System.out.println("案例21:把给定字符串流转化成数组:" + Arrays.asList(newArray1));
        System.out.println("-----------------------");

        //5.4 reduce,规约操作,把一个流的所有元素合并成一个元素,比如求和、求乘积、求最大最小值等。
        //案例22:求整数数组元素之和、乘积和最大值
        List<Integer> list = Arrays.asList(5, 2, 3, 1, 4);
        Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        System.out.println("案例22:数组元素之和:" + sum.get());
        System.out.println("案例22:数组元素乘积:" + product.get());
        System.out.println("案例22:数组元素最大值:" + max.get());

        //案例23:求全班学生最高分、全班学生总分
        List<Student> students = buildStudentList();
        Optional<Integer> maxScore = students.stream().map(r -> r.getScore()).reduce(Integer::max);
        Optional<Integer> sumScore = students.stream().map(r -> r.getScore()).reduce(Integer::sum);
        Optional<Integer> minScore = students.stream().map(r -> r.getScore()).reduce(Integer::min);
        System.out.println("案例23:全班学生最高分:" + maxScore.get());
        System.out.println("案例23:全班学生最低分:" + minScore.get());
        System.out.println("案例23:全班学生总分:" + sumScore.get());
        System.out.println("-----------------------");

        //5.5 collect,把 stream 中的元素归集到新的集合或者归集成单个元素。
        //5.5.1 归集成新集合,方法包括 toList、toSet、toMap。
        //案例24:根据学生列表,归纳出姓名列表、不同分数列表、姓名分数集合,其中 Mike 和 Jessie 的分数都是 88。
        List<String> lists = students.stream().map(r -> r.getName()).collect(Collectors.toList());
        Set<Integer> set = students.stream().map(r -> r.getScore()).collect(Collectors.toSet());
        Map<String, Integer> map = students.stream().collect(Collectors.toMap(Student::getName, Student::getScore));
        System.out.println("案例24:全班学生姓名列表:" + lists);
        System.out.println("案例24:全班学生不同分数列表:" + set);
        System.out.println("案例24:全班学生姓名分数集合:" + map);
        System.out.println("-----------------------");

        //5.5.2 统计功能,统计功能包括如下方法:
        //求总和count,求和summingInt,summingLong,summingDouble
        //求平均值averagingInt,averagingLong,averagingDouble
        //求最大/最小值maxBy,minBy
        //综合统计summarizingInt,summarizingLong,summarizingDouble,

        //案例25:求总数、求和、最大/最小/平均值
        //List list = Arrays.asList(5, 2, 3, 1, 4);
        long count = list.stream().collect(Collectors.counting());
        int summingInt = list.stream().collect(Collectors.summingInt(r -> r));
        double averagingDouble = list.stream().collect(Collectors.averagingDouble(r -> r));
        Optional<Integer> maxBy = list.stream().collect(Collectors.maxBy(Integer::compare));
        Optional<Integer> minBy = list.stream().collect(Collectors.minBy(Integer::compare));
        System.out.println("案例25:总数:" + count);
        System.out.println("案例25:总和:" + summingInt);
        System.out.println("案例25:平均值:" + averagingDouble);
        System.out.println("案例25:最大值:" + maxBy.get());
        System.out.println("案例25:最小值:" + minBy.get());
        System.out.println("-----------------------");

        //5.5.3 分区和分组,主要包括两个函数:partitioningBy:把stream分成两个map,groupingBy:把stream分成多个map
        //案例27:将学生按照 80 分以上和以下分区
        Map<Boolean, List<Student>> partitionByScore = students.stream().collect(Collectors.partitioningBy(x -> x.getScore() > 80));
        System.out.println("案例27:将学生按照考试成绩80分以上分区:" + partitionByScore);
        partitionByScore.forEach((k, v) -> {
            System.out.print(k ? "80分以上:" : "80分以下:");
            v.forEach(r -> System.out.print(r.getName() + ","));
            System.out.println();
        });
        System.out.println("-----------------------");

        //案例28:将学生按照性别、年龄分组
        Map<String, Map<Integer, List<Student>>> group = students.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getScore)));
        System.out.println("案例28:将学生按照性别、年龄分组:");
        group.forEach((k, v) -> {
            System.out.println("male".equals(k) ? "小哥哥:" : "小姐姐:");
            v.forEach((k1, v1) -> {
                System.out.print("  " + k1 + ":");
                v1.forEach(r -> System.out.print(r.getName() + ","));
                System.out.println();
            });
        });
        System.out.println("-----------------------");

        //5.5.4 连接,将 stream 中的元素用指定的连接符合并,连接符可以是空。
        //案例29:输出所有学生的姓名,用逗号分隔,这里还是使用案例 27 中的学生列表
        String studentNames = students.stream().map(r -> r.getName()).collect(Collectors.joining(","));
        System.out.println("案例29:所有学生姓名列表:" + studentNames);
        System.out.println("-----------------------");

        //5.5.5 规约,在 5.4 节已经讲过规约了,这里的规约支持更强大的自定义规约。
        //案例30:数组中每个元素加 1 后求总和
        //List list = Arrays.asList(5, 2, 3, 1, 4);
        int listSum = list.stream().collect(Collectors.reducing(0, x -> x + 1, (sums, b) -> sums + b));
        System.out.println("案例30:数组中每个元素加1后总和:" + listSum);
        System.out.println("-----------------------");

        //5.6 max、min、count,stream 提供的方便统计的方法。
        //案例31:统计整数数组中最大值、最小值、大于3的元素个数
        //List list = Arrays.asList(5, 2, 3, 1, 4);
        System.out.println("案例31:数组元素最大值:" + list.stream().max(Integer::compareTo).get());
        System.out.println("案例31:数组元素最小值:" + list.stream().min(Integer::compareTo).get());
        System.out.println("案例31:数组中大于3的元素个数:" + list.stream().filter(x -> x > 3).count());
        System.out.println("-----------------------");

        //案例32:统计分数最高的学生姓名
        Optional<Student> optional = students.stream().max(Comparator.comparing(r -> r.getScore()));
        System.out.println("案例32:成绩最高的学生姓名:" + optional.get().getName() + ": " + optional.get().getScore());

        System.out.println("-----------end------------");
    }

3. 测试验证

StreamTest 包含以上案例,是个完整的测试案例。

package com.jerry.unit.stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * Stream流测试
 *
 * @author zrj
 * @since 2022/4/27
 **/
public class StreamTest {
    /**
     * 1.创建Stream
     * 1.1 使用集合创建
     * 1.2 使用数组创建
     * 1.3 使用 Stream 静态方法
     */
    @Test
    public void createStream() {
        //1.1使用集合创建
        List<Integer> list = Arrays.asList(5, 2, 3, 1, 4);
        Stream streamInt = list.stream();
        printStream(streamInt, "1.1使用集合创建Stream:");
        System.out.println("-----------------------");

        //1.2使用数组创建
        String[] array = {"ab", "abc", "abcd", "abcde", "abcdef"};
        Stream<String> streamStr = Arrays.stream(array);
        printStream(streamStr, "1.2使用数组创建Stream:");
        System.out.println("-----------------------");

        //1.3使用 Stream 静态方法
        Stream<String> streamStatic = Stream.of("ab", "abc", "abcd", "abcde", "abcdef");
        printStream(streamStatic, "1.3使用 Stream 静态方法Stream:");
        System.out.println("-----------------------");

        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 3).limit(5);
        printStream(stream4, "Stream迭代器:");

        Stream<Integer> stream5 = Stream.generate(new Random()::nextInt).limit(3);
        printStream(stream5, "Stream构建器:");
        System.out.println("-----------------------");

    }

    /**
     * 2.Stream无状态操作
     * 2.1 map
     * 2.2 mapToXXX
     * 2.3 flatMap
     * 2.4 flatMapToXXX
     * 2.5 filter
     * 2.6 peek
     * 2.7 unordered
     */
    @Test
    public void operationStreamStateless() {
        //2.1 map,接收一个函数作为入参,把这个函数应用到每个元素上,执行结果组成一个新的 stream 返回。
        //案例1:对整数数组每个元素加3 :
        List<Integer> list = Arrays.asList(5, 2, 3, 1, 4);
        List<Integer> newList = list.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println("案例1:对整数数组每个元素加3的值:" + newList);

        //案例2:把字符串数组的每个元素转换为大写:
        List<String> listStr = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef");
        List<String> newListStr = listStr.stream().map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("案例2:把字符串数组的每个元素转换为大写:" + newListStr);
        System.out.println("-----------------------");

        //2.2 mapToXXX,包括三个方法:mapToInt、mapToDouble、mapToLong
        //案例3:把字符串数组转为整数数组:
        List<String> listStr3 = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef");
        int[] newList3 = listStr3.stream().mapToInt(r -> r.length()).toArray();
        System.out.println("案例3:把字符串数组转为整数数组: " + Arrays.toString(newList3));
        System.out.println("-----------------------");

        //2.3 flatMap,flatMap接收函数作为入参,然后把集合中每个元素转换成一个stream,再把这些stream组成一个新的stream,是拆分单词很好的工具。
        //案例4:把一个字符串数组转成另一个字符串数组:
        List<String> list4 = Arrays.asList("ab-abc-abcd-abcde-abcdef", "5-2-3-1-4");
        List<String> newList4 = list4.stream().flatMap(s -> Arrays.stream(s.split("-"))).collect(Collectors.toList());
        System.out.println("案例4:把一个字符串数组转成另一个字符串数组 " + newList4);
        System.out.println("-----------------------");

        //2.4 flatMapToXXX,类似于flatMap,返回一个XXXStream。包括三个方法:flatMapToInt、flatMapToLong、flatMapToDouble
        //案例5:对给定的二维整数数组求和:
        int[][] data = {{1, 2}, {3, 4}, {5, 6}};
        IntStream intStream = Arrays.stream(data).flatMapToInt(row -> Arrays.stream(row));
        System.out.println("案例5:对给定的二维整数数组求和:" + intStream.sum());
        System.out.println("-----------------------");

        //2.5 filter,筛选功能,按照一定的规则将符合条件的元素提取到新的流中。
        //案例6:找出考试成绩在 90 分以上的学生姓名:
        List<Student> students = buildStudentList();
        List<String> nameList = students.stream().filter(x -> x.getScore() >= 90).map(Student::getName).collect(Collectors.toList());
        System.out.println("案例6:找出考试成绩在90分以上的学生姓名:" + nameList);
        System.out.println("-----------------------");

        //2.6 peek,
        //返回由 stream 中元素组成的新 stream,用给定的函数作用在新 stream 的每个元素上。
        //传入的函数是一个 Consume 类型的,没有返回值,因此并不会改变原 stream 中元素的值。peek 主要用是 debug,可以方便地 查看流处理结果是否正确。

        //案例7:过滤出 stream 中长度大于 3 的字符串并转为大写:
        List<String> collect = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.print("," + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("->" + e))
                .collect(Collectors.toList());
        System.out.println("案例7:过滤出stream中长度大于3的字符串并转为大写:" + collect);
        System.out.println("-----------------------");

        //2.7 unordered.
        //把一个有序的 stream 转成一个无序 stream ,如果原 stream 本身就是无序的,可能会返回原始的 stream。
        //案例8:把一个有序数组转成无序,每次执行输出的结果不一样。
        System.out.print("案例8:把一个有序数组转成无序:");
        Arrays.asList("1", "2", "3", "4", "5")
                .parallelStream()
                .unordered()
                .forEach(r -> System.out.print(r + " "));
        System.out.println("\n-----------------------");

    }

    /**
     * 3.Stream有状态操作
     * 3.1 distinct
     * 3.2 limit
     * 3.3 skip
     * 3.4 sorted
     */
    @Test
    public void operationStreamState() {
        //3.1 distinct,去重功能。
        //案例9 :去掉字符串数组中的重复字符串
        String[] array = {"a", "b", "b", "c", "c", "d", "d", "e", "e"};
        List<String> newList = Arrays.stream(array).distinct().collect(Collectors.toList());
        System.out.println("案例9 :去掉字符串数组中的重复字符串:" + newList);
        System.out.println("-----------------------");

        //3.2 limit,限制从 stream 中获取前 n 个元素。
        //案例10 :从数组中获取前 5 个元素
        String[] array10 = {"c", "c", "a", "b", "b", "e", "e", "d", "d"};
        List<String> newList10 = Arrays.stream(array10).limit(5).collect(Collectors.toList());
        System.out.println("案例10 :从数组中获取前 5 个元素:" + newList10);
        System.out.println("-----------------------");

        //3.3 skip,跳过 Stream 中前 n 个元素
        //案例11:从数组中获取第5个元素之后的元素
        String[] array11 = {"a", "b", "c", "d", "e", "f", "g", "h", "i"};
        List<String> newList11 = Arrays.stream(array11).skip(5).collect(Collectors.toList());
        System.out.println("案例11:从数组中获取前5个元素之后的元素:" + newList11);
        System.out.println("-----------------------");

        //3.4 sorted,排序功能。
        //案例12:对给定数组进行排序
        String[] array12 = {"c", "c", "a", "b", "b", "e", "e", "d", "d"};
        List<String> newList12 = Arrays.stream(array12).sorted().collect(Collectors.toList());
        System.out.println("案例12:对给定数组进行排序:" + newList12);
        Integer[] array121 = {3, 6, 5, 4, 1, 2, 2, 3, 6};
        List<Integer> newList121 = Arrays.stream(array121).sorted().collect(Collectors.toList());
        System.out.println("案例12:对给定数组进行排序:" + newList121);
        System.out.println("-----------------------");

        //案例13:按照学生成绩进行排序,正序。
        List<Student> students = buildStudentList();
        List<String> nameList = students.stream().sorted(Comparator.comparing(Student::getScore)).map(Student::getName).collect(Collectors.toList());
        System.out.println("案例13:按照学生成绩进行排序:" + nameList);
        System.out.println("-----------------------");
    }

    /**
     * 4.Stream短路操作
     * 4.1 findAny
     * 4.2 anyMatch
     * 4.3 allMatch
     * 4.4 noneMatch
     * 4.5 findFirst
     */
    @Test
    public void operationStreamShortCircuit() {

        //4.1 findAny,找出 stream 中任何一个满足过滤条件的元素。
        //案例14:找出任何一个成绩高于 90 分的学生。排完序取得第一个,最高分。
        List<Student> students = buildStudentList();
        Optional<Student> studentFindAny = students.stream().filter(x -> x.getScore() > 90).findAny();
        System.out.println("案例14:找出任意一个考试成绩在90分以上的学生姓名:" + studentFindAny.orElseGet(null).getName());
        System.out.println("-----------------------");

        //4.2 anyMatch,是否存在任意一个满足给定条件的元素。
        //案例15:是否存在成绩高于 90 分的学生,是否存在成绩低于 50 分的学生。
        boolean result1 = students.stream().anyMatch(x -> x.getScore() > 90);
        System.out.println("案例15:是否存在成绩高于 90 分的学生:" + result1);
        boolean result2 = students.stream().anyMatch(x -> x.getScore() < 50);
        System.out.println("案例15:是否存在成绩低于 50 分的学生:" + result2);
        System.out.println("-----------------------");

        //4.3 allMatch,是否集合中所有元素都满足给定的条件,如果集合是空,则返回 true。
        //案例16:学生成绩是否都高于 90 分,是否都高于 50 分。
        boolean result16 = students.stream().allMatch(x -> x.getScore() > 90);
        System.out.println("案例16:是否所有学生的成绩都高于90分:" + result16);
        boolean result26 = students.stream().allMatch(x -> x.getScore() > 50);
        System.out.println("案例16:是否所有学生的成绩都高于50分:" + result26);
        System.out.println("-----------------------");

        //4.4 noneMatch,是否没有元素能匹配给定的条件,如果集合是空,则返回 true。
        //案例17:是不是没有学生成绩在 90 分以上,是否没有学生成绩在 50 分以下。
        boolean result17 = students.stream().noneMatch(x -> x.getScore() > 90);
        System.out.println("案例17:是不是没有学生成绩在 90 分以上:" + result17);
        boolean result27 = students.stream().noneMatch(x -> x.getScore() < 50);
        System.out.println("案例17:是不是没有学生成绩在 50 分以下:" + result27);
        System.out.println("-----------------------");

        //4.5 findFirst,找出第一个符合条件的元素。
        //案例18:找出第一个成绩在 90 分以上的学生。
        Optional<Student> studentFindFirst = students.stream().filter(x -> x.getScore() > 90).findFirst();
        System.out.println("案例18:第一个成绩在 90 分以上的学生姓名:" + studentFindFirst.orElseGet(null).getName());
        System.out.println("-----------------------");

    }

    /**
     * 5.Stream非短路操作
     * 5.1 forEach
     * 5.2 forEachOrdered
     * 5.3 toArray
     * 5.4 reduce
     * 5.5 collect
     * 5.6 max、min、count
     */
    @Test
    public void operationStreamNonShortCircuit() {
        //5.1 forEach,遍历元素。
        //案例19:遍历一个数组并打印
        List<Integer> array = Arrays.asList(5, 2, 3, 1, 4);
        System.out.print("案例19:遍历一个数组并打印:");
        array.stream().forEach(System.out::print);
        System.out.println("\n-----------------------");

        //5.2 forEachOrdered,按照给定集合中元素的顺序输出。主要使用场景是在并行流的情况下,按照给定的顺序输出元素。
        //案例20:用并行流遍历一个数组并按照给定数组的顺序输出结果
        System.out.print("案例20:用并行流遍历一个数组并按照给定数组的顺序输出结果:");
        array.parallelStream().forEachOrdered(System.out::print);
        System.out.println("\n-----------------------");

        //5.3 toArray,返回包括给定 stream 中所有元素的数组。
        //案例21:把给定字符串流转化成数组
        Stream<String> stream = Arrays.asList("ab", "abc", "abcd", "abcde", "abcdef").stream();
        String[] newArray1 = stream.toArray(str -> new String[5]);
        //String[] newArray2 = stream.toArray(String[]::new);
        //Object[] newArray3 = stream.toArray();
        //注意:stream流只能使用一次就好自动关闭,不能转化多次,所以newArray1,newArray2,newArray3不能同时放开
        //如果要使用多次就要使用复制功能,把stream复制为输入流多次拷贝
        System.out.println("案例21:把给定字符串流转化成数组:" + Arrays.asList(newArray1));
        System.out.println("-----------------------");

        //5.4 reduce,规约操作,把一个流的所有元素合并成一个元素,比如求和、求乘积、求最大最小值等。
        //案例22:求整数数组元素之和、乘积和最大值
        List<Integer> list = Arrays.asList(5, 2, 3, 1, 4);
        Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        System.out.println("案例22:数组元素之和:" + sum.get());
        System.out.println("案例22:数组元素乘积:" + product.get());
        System.out.println("案例22:数组元素最大值:" + max.get());

        //案例23:求全班学生最高分、全班学生总分
        List<Student> students = buildStudentList();
        Optional<Integer> maxScore = students.stream().map(r -> r.getScore()).reduce(Integer::max);
        Optional<Integer> sumScore = students.stream().map(r -> r.getScore()).reduce(Integer::sum);
        Optional<Integer> minScore = students.stream().map(r -> r.getScore()).reduce(Integer::min);
        System.out.println("案例23:全班学生最高分:" + maxScore.get());
        System.out.println("案例23:全班学生最低分:" + minScore.get());
        System.out.println("案例23:全班学生总分:" + sumScore.get());
        System.out.println("-----------------------");

        //5.5 collect,把 stream 中的元素归集到新的集合或者归集成单个元素。
        //5.5.1 归集成新集合,方法包括 toList、toSet、toMap。
        //案例24:根据学生列表,归纳出姓名列表、不同分数列表、姓名分数集合,其中 Mike 和 Jessie 的分数都是 88。
        List<String> lists = students.stream().map(r -> r.getName()).collect(Collectors.toList());
        Set<Integer> set = students.stream().map(r -> r.getScore()).collect(Collectors.toSet());
        Map<String, Integer> map = students.stream().collect(Collectors.toMap(Student::getName, Student::getScore));
        System.out.println("案例24:全班学生姓名列表:" + lists);
        System.out.println("案例24:全班学生不同分数列表:" + set);
        System.out.println("案例24:全班学生姓名分数集合:" + map);
        System.out.println("-----------------------");

        //5.5.2 统计功能,统计功能包括如下方法:
        //求总和count,求和summingInt,summingLong,summingDouble
        //求平均值averagingInt,averagingLong,averagingDouble
        //求最大/最小值maxBy,minBy
        //综合统计summarizingInt,summarizingLong,summarizingDouble,

        //案例25:求总数、求和、最大/最小/平均值
        //List list = Arrays.asList(5, 2, 3, 1, 4);
        long count = list.stream().collect(Collectors.counting());
        int summingInt = list.stream().collect(Collectors.summingInt(r -> r));
        double averagingDouble = list.stream().collect(Collectors.averagingDouble(r -> r));
        Optional<Integer> maxBy = list.stream().collect(Collectors.maxBy(Integer::compare));
        Optional<Integer> minBy = list.stream().collect(Collectors.minBy(Integer::compare));
        System.out.println("案例25:总数:" + count);
        System.out.println("案例25:总和:" + summingInt);
        System.out.println("案例25:平均值:" + averagingDouble);
        System.out.println("案例25:最大值:" + maxBy.get());
        System.out.println("案例25:最小值:" + minBy.get());
        System.out.println("-----------------------");

        //5.5.3 分区和分组,主要包括两个函数:partitioningBy:把stream分成两个map,groupingBy:把stream分成多个map
        //案例27:将学生按照 80 分以上和以下分区
        Map<Boolean, List<Student>> partitionByScore = students.stream().collect(Collectors.partitioningBy(x -> x.getScore() > 80));
        System.out.println("案例27:将学生按照考试成绩80分以上分区:" + partitionByScore);
        partitionByScore.forEach((k, v) -> {
            System.out.print(k ? "80分以上:" : "80分以下:");
            v.forEach(r -> System.out.print(r.getName() + ","));
            System.out.println();
        });
        System.out.println("-----------------------");

        //案例28:将学生按照性别、年龄分组
        Map<String, Map<Integer, List<Student>>> group = students.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getScore)));
        System.out.println("案例28:将学生按照性别、年龄分组:");
        group.forEach((k, v) -> {
            System.out.println("male".equals(k) ? "小哥哥:" : "小姐姐:");
            v.forEach((k1, v1) -> {
                System.out.print("  " + k1 + ":");
                v1.forEach(r -> System.out.print(r.getName() + ","));
                System.out.println();
            });
        });
        System.out.println("-----------------------");

        //5.5.4 连接,将 stream 中的元素用指定的连接符合并,连接符可以是空。
        //案例29:输出所有学生的姓名,用逗号分隔,这里还是使用案例 27 中的学生列表
        String studentNames = students.stream().map(r -> r.getName()).collect(Collectors.joining(","));
        System.out.println("案例29:所有学生姓名列表:" + studentNames);
        System.out.println("-----------------------");

        //5.5.5 规约,在 5.4 节已经讲过规约了,这里的规约支持更强大的自定义规约。
        //案例30:数组中每个元素加 1 后求总和
        //List list = Arrays.asList(5, 2, 3, 1, 4);
        int listSum = list.stream().collect(Collectors.reducing(0, x -> x + 1, (sums, b) -> sums + b));
        System.out.println("案例30:数组中每个元素加1后总和:" + listSum);
        System.out.println("-----------------------");

        //5.6 max、min、count,stream 提供的方便统计的方法。
        //案例31:统计整数数组中最大值、最小值、大于3的元素个数
        //List list = Arrays.asList(5, 2, 3, 1, 4);
        System.out.println("案例31:数组元素最大值:" + list.stream().max(Integer::compareTo).get());
        System.out.println("案例31:数组元素最小值:" + list.stream().min(Integer::compareTo).get());
        System.out.println("案例31:数组中大于3的元素个数:" + list.stream().filter(x -> x > 3).count());
        System.out.println("-----------------------");

        //案例32:统计分数最高的学生姓名
        Optional<Student> optional = students.stream().max(Comparator.comparing(r -> r.getScore()));
        System.out.println("案例32:成绩最高的学生姓名:" + optional.get().getName() + ": " + optional.get().getScore());

        System.out.println("-----------end------------");
    }

    /**
     * 构建学生集合
     */
    private List<Student> buildStudentList() {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Mike", 10, "male", 88));
        students.add(new Student("Jerry", 18, "male", 88));
        students.add(new Student("Jack", 13, "male", 90));
        students.add(new Student("Lucy", 15, "female", 100));
        students.add(new Student("Jessie", 12, "female", 78));
        students.add(new Student("Allon", 16, "female", 92));
        students.add(new Student("Alis", 22, "female", 50));
        return students;
    }

    /**
     * 打印Stream
     *
     * @param stream Stream流
     * @param msg    输出信息
     * @return void
     */
    private void printStream(Stream stream, String msg) {
        System.out.print(msg);
        stream.forEach(i -> System.out.print(i + ","));
        System.out.println();
    }

}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Student {
    //定义一个学生类,包含姓名、年龄、性别、考试成绩四个属性
    private String name;
    private Integer age;
    private String sex;
    private Integer score;
}

你可能感兴趣的:(基础框架,stream流式计算详解)