java8新特性系统学习之Stream API

    技术2024-11-12  5

    Stream 流的概念(自己的理解)

    流就是用来操作数据源的。比如,工厂的流水线,数据源就是一些零件,然后工厂里面有流水线,流水线是做什么的呢?他会对这些零件进行加工,最后得到一个新的产品。对应过来Stream流就是对集合、数组等进行流水线式的操作,最后得到一个新的流,在这个流水线上,我们可以对这个流进行一些操作,比如筛选切片排序等。

    Stream的操作三个步骤

    1、创建一个流 一个数据源(集合、数组),获取到一个流 2、中间操作 一个中间操作链,对数据源的数据进行处理 3、终止操作(终端操作) 一个终止操作、执行中间操作链,并产生一个结果

    一、创建流\获取流

    先准备一个Emp的集合,下面会举例会使用到

    List<Emp> es = Arrays.asList( new Emp("zs", 30, 9999.99), new Emp("lqs", 39, 8888.88), new Emp("yjx", 29, 7777.77), new Emp("ddd", 36, 8888.88), new Emp("xxx", 20, 7777.77) );

    1、可以通过Connection系列集合提供的stream()或者parallelStream()获取流

    List<String> list = new ArrayList<>(); Stream<String> stream = list.stream();

    2、通过Arrays中的静态方法stream()获取数组流

    int[] arr = new int[10]; IntStream str = Arrays.stream(arr);

    3、通过Stream类中的静态方法of()获取流

    Stream<String> stream1 = Stream.of("aa", "bb", "cc");

    1中的数据源是list,2中的数据源是arr,3中的数据源是"aa", “bb”, “cc”

    4、创建无限流 ---- 迭代

    Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2); stream2.limit(10).forEach(System.out::println);

    共产生10个数,从0开始,依次+2*

    创建无限流 -----生成

    Stream.generate(() -> Math.random()) .limit(5) .forEach(System.out::println);

    二、中间操作

    1、filter – 接受Lombda,从流中排出某些元素

    @Test public void test1(){ //中间操作 Stream<Emp> stream = es.stream().filter(e -> e.getAge() > 30); //终止操作 stream.forEach(System.out::println); }

    输出结果

    中间操作,不会执行任何操作,只有当终止操作执行以后,所有的中间操作和终止操作才全部执行,这个过程叫做惰性求值或延迟加载

    2、limit – 截断流,使其元素不超过给定的数量

    @Test public void test2(){ es.stream().filter(e -> e.getSalary() > 8000).limit(2).forEach(System.out::println); }

    对Emp这个集合进行流操作,首先进行过滤,只选工资大于8000,然后得到的结果只选2个并打印

    3、skip() – 跳过元素,返回一个扔掉前n个元素的流,若流中不足n个,则返回一个空流

    @Test public void test3(){ es.stream().filter(e -> e.getSalary() > 5000).skip(4).forEach(System.out::println); }

    对Emp集合进行流操作,先是过滤,取出薪资大于5000的,然后跳过前4个,并打印后面的,如果过滤后的流中的元素不足4个,那你把前4个跳过了,肯定流中就没有元素了,就返回空

    执行结果:本来有5个,跳过前4个,就只打印最后一个 4、distinct – 筛选,通过流所生成元素的hashcode()和equals()去除重复元素 我们在Emp集合中复制几个emp,测试一下去重

    List<Emp> es = Arrays.asList( new Emp("zs", 30, 9999.99), new Emp("lqs", 39, 8888.88), new Emp("yjx", 29, 7777.77), new Emp("ddd", 36, 8888.88), //下面是3条重复数据 new Emp("xxx", 20, 7777.77), new Emp("xxx", 20, 7777.77), new Emp("xxx", 20, 7777.77) ); @Test public void test4(){ es.stream().filter(e -> e.getSalary() > 5000).skip(4).distinct().forEach(System.out::println); }

    输出结果 可以发现我们写了distinct()的,但是并没有去重,那是因为distinct()是通过hashcode()和equals()去除重复元素的,所以我们需要在Emp实体类中重写hashcode()和equals()这两个方法 再次运行就好了

    Map映射(也属于中间操作,重点理解)

    map- 接收Lambda ,将元素转换成其他形式或提取信息。接收一 个函数作为参数,该函数会被应用到每个元素上,并将其映射成- -个新的元素。flatMap- 接收- - 一个函数作为参数,将流中的每个值都换成另-个流,然后把所有流连接成一-个流

    上面听起来可能有点抽象,举例说明更容易理解

    @Test public void test5(){ List<String> list = Arrays.asList("aa", "bb", "cc", "dd", "ee"); list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println); }

    打印结果就是将list所有元素全部转换为大写

    介绍映射说接收一 个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素,所以意思就是他从数据源中的"aa"提取出来应用到map这个函数上,又取出一个"bb"应用到map函数上,把每个元素都应用到map这个函数上,最后产生一个新流,得到一个结果

    再举个例子,我们要提取Emp集合中每个Emp的名字

    @Test public void test6(){ es.stream().map(Emp::getName).forEach(System.out::println); }

    这样就是将es这个集合中的每个Emp都应用到map函数上,使用方法引用获取名字,最后得到一个结果

    排序

    sorted( )一自然排序

    @Test public void test7(){ List<String> list = Arrays.asList("bb", "aa", "cc", "dd", "ee"); list.stream().sorted().forEach(System.out::println); }

    最后打印结果为aa,bb,cc,dd,ee

    sorted(Comparator com)- -定制排序

    @Test public void test8(){ es.stream().sorted((o1,o2) -> { if (o1.getName().equals(o2.getName())){ //如果姓名一样就按年龄排 return o1.getAge().compareTo(o2.getAge()); }else { //否则按姓名排 return o1.getName().compareTo(o2.getName()); } }).forEach(System.out::println); }

    输出结果

    三、终止操作

    1、allMatch-检查是否匹配所有元素

    @Test public void test1(){ boolean match = es.stream().allMatch(emp -> emp.getAge().equals(20L)); System.out.println(match); }

    输出结果为false,意思就是说es这个集合里面是不是所有都匹配年龄等于20,肯定不是啊,我还有36岁,39岁这些呢

    2、anyMatch-检查是否至少匹配一个元素

    @Test public void test2(){ boolean match1 = es.stream().anyMatch(emp -> emp.getName().equals("ddd")); System.out.println(match1); }

    输出结果为true,意思就是es这个集合里面是不是至少有一个姓名是ddd的,只要有就返回true

    3、noneMatch-检查 是否没有匹配所有元素

    @Test public void test3(){ boolean match1 = es.stream().noneMatch(emp -> emp.getName().equals("ddd")); System.out.println(match1); }

    输出结果为false,就是说es这个集合里面是不是没有姓名叫ddd的,但是匹配到了,所以返回false

    4、findFirst-返 回第一个元素

    @Test public void test4(){ Optional<Emp> op = es.stream().sorted((e1, e2) -> -Double.compare(e1.getSalary(), e2.getSalary())).findFirst(); System.out.println(op.get()); }

    意思就是对es这个集合里面的元素的薪资进行一个排序,然后获取第一个元素,这里是获取薪资最高的那个,需要注意的是,他返回的是一个Optional,可以通过get()方法获取Optional中封装的对象

    为什么需要这个Optional呢?

    因为java8尽可能地区避免空指针异常,怎么避免呢?他就设计了一个Optional这样的容器类,把有可能为空的对象封装到这个容器里,那么他就有一些方法可以使用,比如orElse,作用就是,Optional中封装的那个对象为空,你就可以放个替代的对象,这样就避免了空指针异常

    5、findAny返回当 前流中的任意元素 需求:现在我有三个姓名为xxx的程序员,我要随便找个加入到开发团队中

    @Test public void test5(){ Optional<Emp> op = es.parallelStream().filter(e -> e.getName().equals("xxx")).findAny(); System.out.println(op.get()); }

    这里使用的是并行流,可以理解为多个线程同时找,谁先找到就把那个程序员加入到开发团队中,如果使用串行流,他是一个一个找,找到一个就取出来

    6、count-返 回流中元素的总个数

    @Test public void test6(){ System.out.println(es.stream().count()); }

    输出结果为7,就是es集合里面有多少个元素,当然你可以过滤等多个操作

    7、max-返回流中最大值

    @Test public void test7(){ Optional<Emp> op = es.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(op.get()); }

    按照薪资排序找出薪资最大的那个元素

    8、min-返回流中最小值 需求:获取公司中薪资最小的薪资是多少

    @Test public void test8(){ Optional<Double> op = es.stream().map(Emp::getSalary).min(Double::compare); System.out.println(op.get()); }

    输出结果为7777,数据源是es这个集合嘛。先把每个元素应用到map函数上,生成一个新的元素就是每个元素的薪资,然后获取这个流中的元素的最小值,放到Optional容器中,最后获取Optional中的值

    对于Stream API我了解的不是很深,之后有遇到新的知识点,我也会记录下来,stream流的知识点还有很多,下篇继续学习

    Processed: 0.043, SQL: 9