Stream流的常用方法(filter、distinct、limit、skip、map、reduce、anyMatch、allMatch、findAny......)

Stream流的使用

流操作是Java8提供一个重要新特性,它允许开发人员以声明性方式处理集合,其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中,能 让代码更加简洁,极大地简化了集合的处理操作,提高了开发的效率和生产力。

同时stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。在Stream中的操作每一次都会产生新的流,内部不会像普通集合操作一样立刻获取值,而是惰性 取值,只有等到用户真正需要结果的时候才会执行。并且对于现在调用的方法,本身都是一种高层次构件,与线程模型无关。因此在并行使用中,开发者们无需再去操 心线程和锁了。Stream内部都已经做好了

如果刚接触流操作的话,可能会感觉不太舒服。其实理解流操作的话可以对比数据库操作。把流的操作理解为对数据库中 数据的查询操作 
	集合 = 数据表
    元素 = 表中的每条数据 
    属性 = 每条数据的列
    流API = sql查询 

流操作详解

Stream流接口中定义了许多对于集合的操作方法,总的来说可以分为两大类:中间操作和终端操作。

  • 中间操作:会返回一个流,通过这种方式可以将多个中间操作连接起来,形成一个调用链,从而转换为另外 一个流。除非调用链后存在一个终端操作,否则中间操作对流不会进行任何结果处理。

  • 终端操作:会返回一个具体的结果,如boolean、list、integer等。

1、筛选

对于集合的操作,经常性的会涉及到对于集中符合条件的数据筛选,Stream中对于数据筛选两个常见的API: filter(过滤)、distinct(去重)

1.1基于filter()实现数据过

该方法会接收一个返回boolean的函数作为参数,终返回一个包括所有符合条件元素的流。

案例:获取所有年龄20岁以下的学生

/**
 * @author 我是七月呀
 * @date 2020/12/22
 */
public class FilterDemo {
    public static void main(String[] args) {
        
        //获取所有年龄20岁以下的学生
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student(1,19,"张三","M",true));
        students.add(new Student(1,18,"李四","M",false));
        students.add(new Student(1,21,"王五","F",true));
        students.add(new Student(1,20,"赵六","F",false));
        students.stream().filter(student -> student.getAge()<20);
    }
}

源码解析

在这里插入图片描述

此处可以看到filter方法接收了Predicate函数式接口。

在这里插入图片描述

首先判断predicate是否为null,如果为null,则抛出NullPointerException;构建Stream,重写opWrapsink方法。参数flags:下一个sink的标志位,供优化使用。参数sink:下一个sink,通过此参数将sink构造成单链。此时流已经构建好,但是因为begin()先执行,此时是无法确定流中后续会存在多少元素的,所以传递-1,代表无法确定。最后调用Pridicate中的test,进行条件判断,将符合条件数据放入流中。

1.2基于distinct实现数据去重

/**
 * @author 我是七月呀
 * @date 2020/12/22
 */
public class DistinctDemo {
    public static void main(String[] args) {
        
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
        integers.stream().distinct().collect(Collectors.toList());
        
    }
}

源码解析
在这里插入图片描述

根据其源码,我们可以知道在distinct()内部是基于LinkedHashSet对流中数据进行去重,并终返回一个新的流。

2、切片

2.1基于limit()实现数据截取

该方法会返回一个不超过给定长度的流

案例:获取数组的前五位

/**
 * @author 我是七月呀
 * @date 2020/12/22
 */
public class LimitDemo {

    public static void main(String[] args) {
		//获取数组的前五位
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
        integers.stream().limit(5);
       
    }
}

源码解析:

在这里插入图片描述

对于limit方法的实现,它会接收截取的长度,如果该值小于0,则抛出异常,否则会继续向下调用 SliceOps.makeRef()。该方法中this代表当前流,skip代表需要跳过元素,比方说本来应该有4个元素,当跳过元素 值为2,会跳过前面两个元素,获取后面两个。maxSize代表要截取的长度

在这里插入图片描述

在makeRef方法中的unorderedSkipLimitSpliterator()中接收了四个参数Spliterator,skip(跳过个数)、limit(截取 个数)、sizeIfKnown(已知流大小)。如果跳过个数小于已知流大小,则判断跳过个数是否大于0,如果大于则取截取 个数或已知流大小-跳过个数的两者小值,否则取已知流大小-跳过个数的结果,作为跳过个数。
后对集合基于跳过个数和截取个数进行切割。

2.2基于skip()实现数据跳过

案例:从集合第三个开始截取5个数据

/**
 * @author 我是七月呀
 * @date 2020/12/22
 */
public class LimitDemo {

    public static void main(String[] args) {
        //从集合第三个开始截取5个数据
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
        List<Integer> collect = integers.stream().skip(3).limit(5).collect(Collectors.toList());
        collect.forEach(integer -> System.out.print(integer+" "));

    }
}
结果4 4 5 5 6

案例:先从集合中截取5个元素,然后取后3个

/**
 * @author 我是七月呀
 * @date 2020/12/22
 */
public class LimitDemo {

    public static void main(String[] args) {
        //先从集合中截取5个元素,然后取后3个
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
        List<Integer> collect = integers.stream().limit(5).skip(2).collect(Collectors.toList());
        collect.forEach(integer -> System.out.print(integer+" "));

    }
}
结果:3 4 4

源码分析:

在这里插入图片描述

在skip方法中接收的n代表的是要跳过的元素个数,如果n小于0,抛出非法参数异常,如果n等于0,则返回当前 流。如果n小于0,才会调用makeRef()。同时指定limit参数为-1.

在这里插入图片描述

此时可以发现limit和skip都会进入到该方法中,在确定limit值时,如果limit<0,则获取已知集合大小长度-跳过的长度。最终进行数据切割。

3、映射

在对集合进行操作的时候,我们经常会从某些对象中选择性的提取某些元素的值,就像编写sql一样,指定获取表 中特定的数据列

 #指定获取特定列 SELECT name FROM student

在Stream API中也提供了类似的方法,map()。它接收一个函数作为方法参数,这个函数会被应用到集合中每一个 元素上,并终将其映射为一个新的元素。
案例:获取所有学生的姓名,并形成一个新的集合

/**
 * 
  • 16
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值