JavaSE——Stream流

目录

一、Stream流的引入

二、Stream流基本介绍

(一)Stream流的作用

(二)Stream流的使用步骤

三、不同数据获取Stream流的方法

(一)单列集合获取Stream流

(二)双列集合获取Stream流

(三)数组获取Stream流

(四)零散数据获取Stream流

四、Stream流的中间方法详解

(一)filter过滤

(二)limit与skip方法

(三)distinct与concat方法

(四)map转换流中的数据类型

五、Stream流的终结方法

(一)forEach、count和toArray方法

(二)collect方法

1.收集到List集合当中

2.收集到Set集合当中

3.收集到Map集合当中

六、总结

七、练习题

(一)数字过滤

(二)字符串过滤并收集

(三)自定义对象过滤并收集


一、Stream流的引入

需求:

1.把所有以"张"开头的元素存储到新集合中

2.把"张"开头的,长度为3的元素再存储到新集合中

3.遍历打印最终结果

代码实现:

java 复制代码
public class StreamDemo1 {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("张无忌");
        list1.add("周芷若");
        list1.add("赵敏");
        list1.add("张强");
        list1.add("张三丰");

        // 使用Stream流操作:
        list1.stream()
                .filter(name -> name.startsWith("张"))
                .filter(name -> name.length() == 3)
                .forEach(name -> System.out.println(name));
        // 张无忌
        // 张三丰

      /*  // 1.把所有以"张"开头的元素存储到新集合中
        ArrayList<String> list2 = new ArrayList<>();
        for (String name : list1) {
            if(name.startsWith("张")){
                list2.add(name);
            }
        }
        // 2.把"张"开头的,长度为3的元素再存储到新集合中
        ArrayList<String> list3 = new ArrayList<>();
        for (String name : list2) {
            if(name.length() == 3){
                list3.add(name);
            }
        }

        // 3.遍历打印最终结果
        for (String name : list3) {
            System.out.println(name);
        }*/
    }
}

二、Stream流基本介绍

(一)Stream流的作用

结合了Lambda表达式,简化集合、数组的操作

(二)Stream流的使用步骤

1.先得到一条Stream流,并把数据放上去

2.利用Stream流中的API进行各种操作

三、不同数据获取Stream流的方法

(一)单列集合获取Stream流

java 复制代码
public class StreamDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c", "d", "e");

        /*
        // 获取到一条流水线,并把集合中的数据放到流水线上
        Stream<String> stream1 = list.stream();

        // 使用终结方法打印一下流水线上的所有数据
        stream1.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                // s:依次表示流水线上的每一个数据
                System.out.println(s);
            }
        });
        */

        // 因为ArrayList是Collection接口的实现类,所以可以用ArrayList直接调用stream流
        list.stream().forEach(s -> System.out.println(s));
        // 简化
        list.forEach(s -> System.out.println(s));
    }
}

运行结果:

(二)双列集合获取Stream流

双列集合不能直接使用Stream流,要先将双列集合转为单列集合。

java 复制代码
public class StreamDemo3 {
    public static void main(String[] args) {
        // 1.创建双列集合
        HashMap<String,Integer> hm = new HashMap<>();
        // 2.添加数据
        hm.put("aaa",111);
        hm.put("bbb",222);
        hm.put("ccc",333);
        hm.put("ddd",444);

        // 3.第一种获取stream流:获取所有的key
         hm.keySet().stream().forEach(s -> System.out.println(s));

        System.out.println("----------------------");
        // 4.第二种获取stream流:获取所有的键值对对象
        hm.entrySet().stream().forEach(s-> System.out.println(s));
    }
}

运行结果:

(三)数组获取Stream流

java 复制代码
public class StreamDemo4 {
    public static void main(String[] args) {
        // 1.创建数组
        int[] arr1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        String[] arr2 = {"a", "b", "c"};

        // 2.获取stream流
        // 基本数据类型
        Arrays.stream(arr1).forEach(s-> System.out.println(s));
        System.out.println("============================");
        // 引用数据类型
        Arrays.stream(arr2).forEach(s-> System.out.println(s));
        System.out.println("============================");

        // 注意:Stream接口中静态方法of的细节:
        // 方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组
        // 但是数组内容必须是引用数据类型的,如果传递基本数据类型,会把整个数组当做一个元素,放到Stream当中。
        Stream.of(arr1).forEach(s -> System.out.println(s)); // [I@41629346
    }
}

运行结果:

(四)零散数据获取Stream流

前提:零散数据必须是同一数据类型。

java 复制代码
public class StreamDemo5 {
    public static void main(String[] args) {
        Stream.of(1, 2, 3, 4, 5).forEach(s -> System.out.println(s));
        
        System.out.println("============================");

        Stream.of("a", "b", "c", "d", "e").forEach(s -> System.out.println(s));
    }
}

运行结果:

四、Stream流的中间方法详解

  1. 中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
  2. 修改Stream流中的数据,不会影响原来集合或数组中的数据

(一)filter过滤

java 复制代码
public class StreamDemo6 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");

        // filter   过滤  把张开头的留下,其余数据过滤不要
        /*list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //如果返回值为true,表示当前数据留下
                //如果返回值为false,表示当前数据舍弃不要
                return s.startsWith("张");
            }
        }).forEach(s -> System.out.println(s));
        */
        // 链式编程+lambda表达式
        list.stream()
                .filter(s -> s.startsWith("张"))
                .filter(s -> s.length() == 3)
                .forEach(s -> System.out.println(s));
        // 张无忌
        // 张三丰
        // 张翠山

        System.out.println("====================================");
        System.out.println(list);
        // stream流的使用不影响原来集合或数组的数据
        // [张无忌, 周芷若, 赵敏, 张强, 张三丰, 张翠山, 张良, 王二麻子, 谢广坤]
    }
}

(二)limit与skip方法

java 复制代码
public class StreamDemo6 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");

        // 获取前3个元素:
        list.stream().limit(3).forEach(s -> System.out.println(s));
        /**
         * 张无忌
         * 周芷若
         * 赵敏
         */

        // 跳过前4个元素打印:
        list.stream().skip(4).forEach(s -> System.out.println(s));
        /**
         * 张三丰
         * 张翠山
         * 张良
         * 王二麻子
         * 谢广坤
         */

        
        // 练习:打印 "张强", "张三丰", "张翠山"
        // 第一种思路:
        // 先获取前面6个元素:"张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山",
        // 然后跳过前面3个元素
         list.stream().limit(6).skip(3).forEach(s -> System.out.println(s));

        // 第二种思路:
        // 先跳过3个元素:"张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤"
        // 然后再获取前面3个元素:"张强", "张三丰", "张翠山"
        list.stream().skip(3).limit(3).forEach(s-> System.out.println(s));
    }
}

(三)distinct与concat方法

distinct:元素去重,依赖(hashCode和equals方法)
注意:引用数据类型的去重要保证类中已经重写了equals方法
distinct底层是用HashSet来去重的,所以传入的对象也要重写hashCode方法

concat合并两个流时要注意,尽可能保持两个流的数据一致,否则会合并成两个流的父类

java 复制代码
public class StreamDemo7 {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "张无忌", "张无忌", "张无忌", "张强",
                "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");

        ArrayList<String> list2 = new ArrayList<>();
        Collections.addAll(list2, "周芷若", "赵敏");

        list1.stream().distinct().forEach(s -> System.out.println(s));
        /**
         * 张无忌
         * 张强
         * 张三丰
         * 张翠山
         * 张良
         * 王二麻子
         * 谢广坤
         */
       
        Stream.concat(list1.stream(), list2.stream()).forEach(s -> System.out.println(s));
    }
}

(四)map转换流中的数据类型

第一个类型:流中原本的数据类型

第二个类型:要转成之后的类型,注意要写成引用数据类型

apply的形参s:依次表示流里面的每一个数据

返回值:表示转换之后的数据,注意要写成引用数据类型

当map方法执行完毕之后,流上的数据就变成了整数

所以在下面forEach当中,s依次表示流里面操作完毕的每一个数据,这个数据现在就是整数了

java 复制代码
public class StreamDemo8 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌-15", "周芷若-14", "赵敏-13",
                "张强-20", "张三丰-100", "张翠山-40", "张良-35", "王二麻子-37", "谢广坤-41");
        // 需求:只获取里面的年龄并进行打印
        // String->int
      
        list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                String[] arr = s.split("-");
                String ageString = arr[1];
                int age = Integer.parseInt(ageString);
                return age;
            }
        }).forEach(s -> System.out.println(s));

        System.out.println("-----------lambda表达式-------------");
        list.stream()
                .map(s -> Integer.parseInt(s.split("-")[1]))
                .forEach(s -> System.out.println(s));
    }
}

五、Stream流的终结方法

(一)forEach、count和toArray方法

java 复制代码
public class StreamDemo9 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强",
                "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");

        // void forEach(Consumer action)           遍历
        System.out.println("------------forEach方法------------");
        // Consumer的泛型:表示流中数据的类型
        // accept方法的形参s:依次表示流里面的每一个数据
        // 方法体:对每一个数据的处理操作(打印)
        /*list.stream().forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });*/
        // lambda表达式
        list.stream().forEach(s -> System.out.println(s));


        System.out.println("------------count方法------------");
        long count = list.stream().count();
        System.out.println(count); // 9


        System.out.println("------------toArray方法------------");
        // 放入Object类型的数组中
        Object[] arr1 = list.stream().toArray();
        System.out.println(Arrays.toString(arr1));
        // [张无忌, 周芷若, 赵敏, 张强, 张三丰, 张翠山, 张良, 王二麻子, 谢广坤]

        // 放入指定类型的数组中
        
        // IntFunction的泛型:具体类型的数组
        // apply的形参:流中数据的个数,要跟数组的长度保持一致
        // apply的返回值:具体类型的数组
        // 方法体:就是创建数组

        // toArray方法的参数的作用:负责创建一个指定类型的数组
        // toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组当中
        // toArray方法的返回值:是一个装着流里面所有数据的数组

       /* String[] arr = list.stream().toArray(new IntFunction<String[]>() {
            @Override
            public String[] apply(int value) {
                return new String[value];
            }
        });
        System.out.println(Arrays.toString(arr));*/

        String[] arr2 = list.stream().toArray(value -> new String[value]);
        System.out.println(Arrays.toString(arr2));
        // [张无忌, 周芷若, 赵敏, 张强, 张三丰, 张翠山, 张良, 王二麻子, 谢广坤]
    }
}

(二)collect方法

1.收集到List集合当中

java 复制代码
public class StreamDemo10 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌-男-15", "周芷若-女-14",
        "赵敏-女-13", "张强-男-20", "张三丰-男-100", "张翠山-男-40",
        "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");


        // 收集List集合当中
        // 需求:把所有的男性收集起来
        List<String> newList1 = list
                .stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toList());
        System.out.println(newList1);
        // [张无忌-男-15, 张强-男-20, 张三丰-男-100, 张翠山-男-40,
        // 张良-男-35, 王二麻子-男-37, 谢广坤-男-41]
    }
}

2.收集到Set集合当中

java 复制代码
public class StreamDemo10 {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "张无忌-男-15", "张无忌-男-15",
        "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20",
        "张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");

        // 收集Set集合当中可以去重
        // 需求:把所有的男性收集起来
        Set<String> newList2 = list1
                .stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toSet());
        System.out.println(newList2);
        // [张强-男-20, 张良-男-35, 张三丰-男-100, 张无忌-男-15, 
        // 谢广坤-男-41, 张翠山-男-40, 王二麻子-男-37]
    }
}

3.收集到Map集合当中

java 复制代码
public class StreamDemo10 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌-男-15", "周芷若-女-14",
                "赵敏-女-13", "张强-男-20", "张三丰-男-100", "张翠山-男-40",
                "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");

        // 收集Map集合当中    注意:键不能重复,否则会报错
        // 要规定:谁作为键,谁作为值.
        // 把所有的男性收集起来,设置  键:姓名。 值:年龄
        System.out.println("----------------------------------");
        Map<String, Integer> map = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                /*
                 *   toMap : 参数一表示键的生成规则
                 *           参数二表示值的生成规则
                 *
                 * 参数一:
                 *       Function泛型一:表示流中每一个数据的类型
                 *               泛型二:表示Map集合中键的数据类型
                 *
                 *        方法apply形参:依次表示流里面的每一个数据
                 *               方法体:生成键的代码
                 *               返回值:已经生成的键
                 *
                 *
                 * 参数二:
                 *        Function泛型一:表示流中每一个数据的类型
                 *                泛型二:表示Map集合中值的数据类型
                 *
                 *       方法apply形参:依次表示流里面的每一个数据
                 *               方法体:生成值的代码
                 *               返回值:已经生成的值
                 *
                 * */
                .collect(Collectors.toMap(new Function<String, String>() {
                                              @Override
                                              public String apply(String s) {
                                                  //张无忌-男-15
                                                  return s.split("-")[0];
                                              }
                                          },
                        new Function<String, Integer>() {
                            @Override
                            public Integer apply(String s) {
                                return Integer.parseInt(s.split("-")[2]);
                            }
                        }));
        System.out.println(map);
        // {张强=20, 张良=35, 张翠山=40, 王二麻子=37, 张三丰=100, 张无忌=15, 谢广坤=41}

        // lambda表达式
        Map<String, String> map2 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toMap(
                        k -> k.split("-")[0],
                        v -> v.split("-")[2]
                ));

        System.out.println(map2);
        // {张强=20, 张良=35, 张翠山=40, 王二麻子=37, 张三丰=100, 张无忌=15, 谢广坤=41}
    }
}

六、总结

七、练习题

(一)数字过滤

定义一个集合,并添加一些整数 1,2,3,4,5,6,7,8,9,10

过滤奇数,只留下偶数。并将结果保存起来

代码实现:

java 复制代码
@Test
public void test1() {
    ArrayList<Integer> list = new ArrayList<>();
    Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    List<Integer> collect = list.stream()
            .filter(s -> s % 2 == 0)
            .collect(Collectors.toList());
    System.out.println(collect);
    // [2, 4, 6, 8, 10]
}

(二)字符串过滤并收集

创建一个ArrayList集合,并添加以下字符串,字符串中前面是姓名,后面是年龄

"zhangsan,23"

"lisi,24"

"wangwu,25"

保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值

代码实现:

java 复制代码
@Test
public void test2() {
    ArrayList<String> list = new ArrayList<>();
    Collections.addAll(list, "zhangsan,23", "lisi,24", "wangwu,25");
    Map<String, String> collect = list.stream()
            .filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
            .collect(Collectors.toMap(
                    k -> k.split(",")[0],
                    v -> v.split(",")[1]
            ));
    System.out.println(collect);
    // {lisi=24, wangwu=25}
}

(三)自定义对象过滤并收集

现在有两个ArrayList集合,分别存储6名男演员的名字和年龄以及6名女演员的名字和年龄。

姓名和年龄中间用逗号隔开。

比如:张三,23

要求完成如下的操作:

1,男演员只要名字为3个字的前两人

2,女演员只要姓杨的,并且不要第一个

3,把过滤后的男演员姓名和女演员姓名合并到一起

4,将上一步的演员信息封装成Actor对象。

5,将所有的演员对象都保存到List集合中。

备注:演员类Actor,属性有:name,age

男演员: "蔡坤坤,24" , "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27"

女演员: "赵小颖,35" , "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33"

代码实现:

java 复制代码
@Test
public void test3() {
    ArrayList<String> manList = new ArrayList<>();
    ArrayList<String> womenList = new ArrayList<>();
    Collections.addAll(manList, "蔡坤坤,24", "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27");
    Collections.addAll(womenList, "赵小颖,35", "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33");
    // 1.男演员只要名字为3个字的前两人
    Stream<String> stream1 = manList.stream()
            .limit(2)
            .filter(s -> s.split(",")[0].length() == 3);
    // 2.女演员只要姓杨的,并且不要第一个
    Stream<String> stream2 = womenList.stream()
            .skip(1)
            .filter(s -> s.split(",")[0].startsWith("杨"));
    // 3.把过滤后的男演员姓名和女演员姓名合并到一起
    List<Actor> collect = Stream.concat(stream1, stream2)
            .map(
                    s -> new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1]))
            ).collect(Collectors.toList());
    System.out.println(collect);
}
s Actor {
private String name;
private int age;
public Actor(String name, int age) {
    this.name = name;
    this.age = age;
}
@Override
public String toString() {
    return "Actor{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
}
相关推荐
天天扭码5 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶6 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺10 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序18 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜35 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、36 分钟前
Spring Boot 注解
java·spring boot
java亮小白199741 分钟前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列
武子康1 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康1 小时前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql