• Java Stream流的详解


    1. Stream流简介

    1.1 Stream流概念

    Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。
    Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作(bulk data operation)。

    Stream API 借助于同样新出现的 Lambda 表达式,而且使用并发模式,极大的提高编程效率和程序可读性。

    1.2 体验Stream流

    有下面的需求:

    • 创建一个集合,存储多个字符串元素
    • 把集合中所有以"张"开头的元素存储到一个新的集合
    • 把"张"开头的集合中的长度为3的元素存储到一个新的集合
    • 遍历上一步得到的集合

    我们按照之前的原始方式编写代码如下:

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("赵丽颖");
            list.add("玉如梦");
            list.add("林若溪");
            list.add("柳岩");
            list.add("张晓龙");
            list.add("张梦");
    
            //把集合中所有以"张"开头的元素存储到一个新的集合
            ArrayList<String> zhangList = new ArrayList<String>();
    
            for(String s : list) {
                if(s.startsWith("张")) {
                    zhangList.add(s);
                }
            }
    
    //        System.out.println(zhangList);
            //把"张"开头的集合中的长度为3的元素存储到一个新的集合
            ArrayList<String> threeList = new ArrayList<String>();
    
            for(String s : zhangList) {
                if(s.length() == 3) {
                    threeList.add(s);
                }
            }
    
    //        System.out.println(threeList);
            //遍历上一步得到的集合
            for(String s : threeList) {
                System.out.println(s);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    最后结果只有:张晓龙
    在这里插入图片描述

    我们使用Stream流修改这个代码如下:

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("赵丽颖");
            list.add("玉如梦");
            list.add("林若溪");
            list.add("柳岩");
            list.add("张晓龙");
            list.add("张梦");
    
            //Stream流来改进
            list.stream().filter(s -> s.startsWith("张"))
                    .filter(s -> s.length() == 3)
                    .forEach(System.out::println);
                 // .forEach(item-> System.out.println(item));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果一样
    在这里插入图片描述

    1.3 Stream流的好处

    • 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印
    • Stream流把真正的函数式编程风格引入到Java中
    • 代码简洁

    1.4 集合和流对比

    集合和流,同时承载一些表面的相似性,有不同的目标。集合主要关心的是有效的管理和访问他们的元素。相比之下,流不直接访问或操纵元素,它只是提供了一种手段,不关心声明的描述,不关心它们的来源,计算的操作将被执行在原数据中。

    2. Stream流三部曲

    2.1 三部曲执行流程

    Stream操作需要三步,每一步及其相关操作过程如下:

    三部曲执行流程
    生成Stream流创建一条流水线,并把数据放到流水线上准备进行操作
    中间方法进行流水线上的操作
    一次操作完毕之后,还可以继续进行其他操作
    终结方法一个Stream流只能有一个终结方法
    是流水线上的最后一个操作

    2.2 Stream流的思想

    Stream流的思想:获取方法获得数据流,中间方法一步步的操作数据流,终结方法最终输出数据流

    在这里插入图片描述


    下面我们来详细介绍一下每个方法

    3. Stream流的生成方式

    3.1 Collection体系集合

    Collection体系集合可以使用默认方法stream()生成流:

    default Stream<E> stream()
    
    • 1

    例如,List生成流:

    List<String> list = new ArrayList<String>();
    Stream<String> listStream = list.stream();
    
    • 1
    • 2

    3.2 Map体系集合

    有两种方法:
    方法一:Map体系集合需要把Map转成Set集合,需要间接的生成流,具体步骤是先将key与value单独的生成流,再合并

    例如:

    Map<String,Integer> map = new HashMap<String, Integer>();
    Stream<String> keyStream = map.keySet().stream();
    Stream<Integer> valueStream = map.values().stream();
    Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
    
    • 1
    • 2
    • 3
    • 4

    方法二: 通过Map.Entry 直接的生成流

    例如:

    Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
    
    • 1

    3.3 数组

    通过Arrays中的静态方法stream生成流

    语法如下:

    Arrays.stream(数组);
    
    • 1

    3.4 同种数据类型的多个数据

    通过Stream接口的静态方法of(T… values)生成流

    语法如下:

    Stream.of(T... values)
    
    • 1

    例如:

    Stream<Integer> intStream = Stream.of(10, 20, 30);
    
    • 1

    3.5 Stream流生成综合案例

    代码如下:

    package com.test;
    
    import java.util.*;
    import java.util.stream.Stream;
    
    public class StreamDemo {
        public static void main(String[] args) {
            //Collection体系的集合可以使用默认方法stream()生成流
            System.out.println("Collection体系的集合=============================");
            List<String> list = new ArrayList<String>();
            list.add("苏映雪");
            list.add("玉如梦");
            Stream<String> listStream = list.stream();
            listStream.filter(s -> s.endsWith("梦"))
                    .forEach(s -> System.out.println(s));
    
            Set<String> set = new HashSet<>();
            Stream<String> setStream = set.stream();
    
            // Map体系的集合间接的生成流
            System.out.println("Map体系的集合=============================");
            Map<String,Integer> map = new HashMap<String, Integer>();
            map.put("玉如梦",325);
            map.put("岁月枯荣",23);
            map.put("如梭如梦",18);
            Stream<String> keyStream = map.keySet().stream();
            Stream<Integer> valueStream = map.values().stream();
            keyStream.filter(s -> s.contains("如梦"))
                    .forEach(s -> System.out.println(s));
            valueStream.filter(s -> s>100)
                    .forEach(s -> System.out.println(s));
    
            // Map体系的集合直接的生成流
            Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
            entryStream.filter( stringIntegerEntry -> {
                boolean a = stringIntegerEntry.getKey().contains("玉如梦");
                boolean b = stringIntegerEntry.getValue()>200;
                return a&&b;
            }).forEach(stringIntegerEntry -> {
                System.out.println("天之娇女:"+stringIntegerEntry.getKey()+" "+stringIntegerEntry.getValue()+"岁");
            });
    
            //数组可以通过Arrays中的静态方法stream生成流
            System.out.println("数组=============================");
            String[] strArray = {"hello","world","java"};
            Stream<String> strArrayStream = Arrays.stream(strArray);
            strArrayStream.filter(s -> s.contains("o")).forEach(s -> System.out.println(s));
    
            //同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流
            System.out.println("同种数据类型的多个数据=============================");
            Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
            Stream<Integer> intStream = Stream.of(10, 20, 30);
            intStream.filter(integer -> integer>15).forEach(integer -> System.out.println(integer.toString()+","));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    运行结果如下:
    在这里插入图片描述

    4. Stream流中间操作方法

    概念

    • 中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作

    常见方法

    方法名说明
    Stream filter(Predicate predicate)用于对流中的数据进行过滤
    Stream limit(long maxSize)返回此流中的元素组成的流,截取 前指定参数(maxSize)个 数的数据
    Stream skip(long n)跳过指定参数个数的数据,返回由该流的剩余元素组成的流
    static Stream concat(Stream a, Stream b)合并a和b两个流为一个流
    Stream distinct()返回由该流的不同元素(根据Object.equals(Object) )组成的流

    4.1 filter 代码实操

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("赵丽颖");
            list.add("玉如梦");
            list.add("林若溪");
            list.add("柳岩");
            list.add("如梦");
            list.add("张无忌");
    
            //需求1:把list集合中以张开头的元素在控制台输出
            list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
            System.out.println("==============================");
    
            //需求2:把list集合中长度为3的元素在控制台输出
            list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
            System.out.println("==============================");
    
            //需求3:把list集合中以张开头的,长度为3的元素在控制台输出
            list.stream().filter(s -> s.contains("如梦")).filter(s -> s.length() == 3).forEach(System.out::println);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    4.2 limit&skip代码演示

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("赵丽颖");
            list.add("玉如梦");
            list.add("林若溪");
            list.add("柳岩");
            list.add("如梦");
            list.add("张无忌");
    
            Stream<String> listStream = list.stream();
            
            //需求1:取前3个数据在控制台输出
            listStream.limit(3).forEach(System.out::println);
            System.out.println("==============================");
    
            //需求2:跳过3个元素,把剩下的元素在控制台输出
            listStream.skip(3).forEach(System.out::println);
            System.out.println("==============================");
    
            //需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
            listStream.skip(2).limit(2).forEach(System.out::println);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    运行结果如下:
    在这里插入图片描述

    4.3 concat&distinct代码演示

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("赵丽颖");
            list.add("玉如梦");
            list.add("林若溪");
            list.add("柳岩");
            list.add("玉如梦");
            list.add("林若溪");
    
            //需求1:取前4个数据组成一个流
            Stream<String> s1 = list.stream().limit(4);
    
            //需求2:跳过2个数据组成一个流
            Stream<String> s2 = list.stream().skip(2);
    
            //需求3:合并需求1和需求2得到的流,并把结果在控制台输出
    //        Stream.concat(s1,s2).forEach(System.out::println);
    
            //需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
            Stream.concat(s1,s2).distinct().forEach(System.out::println);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    运行结果如下:
    在这里插入图片描述

    5. Stream流终结操作方法

    概念

    • 终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作

    常见方法

    方法名说明
    void forEach(Consumer action)对此流的每个元素执行操作
    long count()返回此流中的元素数

    5.1 Stream流终结方法代码示例

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("赵丽颖");
            list.add("玉如梦");
            list.add("林若溪");
            list.add("玉晴儿");
            list.add("曲华裳");
            list.add("张晓婷");
    
            //需求1:把集合中的元素在控制台输出
            list.stream().forEach(System.out::println);
    
            //需求2:统计集合中有几个以张开头的元素,并把统计结果在控制台输出
            long count = list.stream().filter(s -> s.startsWith("玉")).count();
            System.out.println(count);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果如下:
    在这里插入图片描述

    6. Stream流的收集操作

    概念

    • 对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中

    常用方法

    方法名说明
    R collect(Collector collector)把结果收集到集合中

    同时工具类Collectors提供了具体的收集方式

    方法名说明
    public static Collector toList()把元素收集到List集合中
    public static Collector toSet()把元素收集到Set集合中
    public static Collector toMap(Function keyMapper,Function valueMapper)把元素收集到Map集合中

    6.1 Stream流收集代案例

    public class CollectDemo {
        public static void main(String[] args) {
            //创建List集合对象
            System.out.println("List集合收集操作============================");
            List<String> list = new ArrayList<String>();
            list.add("玉如梦");
            list.add("林若溪");
            list.add("曲华裳");
            list.add("祝晴");
    
            Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
    
            List<String> names = listStream.collect(Collectors.toList());
            for (String name : names) {
                System.out.println(name);
            }
    
            //创建Set集合对象
            System.out.println("Set集合收集操作============================");
            Set<Integer> set = new HashSet<Integer>();
            set.add(10);
            set.add(20);
            set.add(30);
            set.add(33);
            set.add(35);
    
            // 得到年龄大于25的流
            Stream<Integer> setStream = set.stream().filter(age -> age > 25);
    
            // 把使用Stream流操作完毕的数据收集到Set集合中并遍历
            Set<Integer> ages = setStream.collect(Collectors.toSet());
            for (Integer age : ages) {
                System.out.println(age);
            }
    
            // 定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
            System.out.println("Map集合收集操作============================");
            String[] strArray = {"曲华裳,30", "玉如梦,35", "林若溪,33", "祝晴,25"};
    
            // 得到字符串中年龄数据大于28的流
            Stream<String> arrayStream = Stream.of(strArray)
                    .filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
    
            // 把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
            Map<String, Integer> map = arrayStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
    
            Set<String> keySet = map.keySet();
            for (String key : keySet) {
                Integer value = map.get(key);
                System.out.println(key + "," + value);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    运行结果如下:
    在这里插入图片描述

  • 相关阅读:
    Linux磁盘管理:最佳实践
    使用spring-ai快速对接ChatGpt
    with ldid... /opt/MonkeyDev/bin/md: line 326: ldid: command not found
    SpringApplication对应可配置属性作用
    app发布动态解决图片适配问题(等比展示)
    【Debug】关于 nginx 上传文件时出现 413 及 500 错误码解决方法
    Nifi02 部署与入门
    工作失误合集,这个月的工资被扣没咯!
    Android修改分区格式为F2FS
    ElasticSearch - 索引库和文档相关命令操作
  • 原文地址:https://blog.csdn.net/weixin_45525272/article/details/125837882