Stream API的特性

stream api

说到Stream便容易想到I/O Stream,而实际上我们这里讲的Stream它是Java8中对数据处理的一种抽象描述.
我们可以把它理解为数据的管道,我们可以通过这条管道提供给我们的API很方便的对里面的数据进行复杂的操作! 比如过滤,收集,分组分区等等.并且更强大的是能够方便的进行并行操作.

stream 的定义

流到底是什么呢?简短的定义就是“根据支持数据处理操作的源生成的元素序列”。

●元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。流的目的在于表达计算,比如filter、sorted和map。集合讲的是数据(存储),流讲的是计算(处理)

●源——流会使用一个提供数据的源,如集合、数组等。

●数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。

stream 操作的特点

●流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
通过stream的这个操作特性,我们可以方便的使用链式编程(只要这个操作方法返回类型为stream即可),将多个操作联系起来.非常类似于数据库操作的sql语法(从哪张表查,查询条件是什么,查询哪些列的数据,排序规则如何等等),流水线的操作可以看作对数据源进行数据库式查询

●内部迭代——与使用迭代器外部迭代的集合不同,流的迭代操作是在背后进行的。
使用迭代器对集合数据进行迭代.这种方式有一定的缺陷:需要将如何迭代的代码与逻辑代码进行耦合.

// 需求: 从学生名字列表中,筛选出姓李的学生,再筛选出名字是三个字的学生,最后将结果打印
List<String> temp = new ArrayList<>();
for (String name : list) {
    if (name.startsWith("李")) {// 通过姓氏筛选
        temp.add(name);
    }
}
List<String> temp1 = new ArrayList<>();
for (String name : temp) {
    if (name.length() == 3) {// 通过名字长度筛选
        temp1.add(name);
    }
}
for (String string : temp1) {// 打印结果
    System.out.println(string);
}

对于上面的案例,我们使用传统的方式来做,看起来没有问题,但是,实现之后,回到本质上来,其实我们的需求仅仅是想做三件事而已,但是因为各种语法规范,我们不得不写出循环,再在循环中加入我们的逻辑.
比如,如果我现在需求改变,不想通过这两个条件筛选,那么,我们现在的代码将会改动很大.这就是耦合.

而,使用stream的方式,真正的舍弃”怎么做”,而回归到”做什么“上来.

list.stream().
    filter(x -> x.startsWith("李")).// 筛选姓氏
    filter(x -> x.length() == 3).// 筛选名字长度
    forEach(System.out::println);// 打印

显而易见的,使用stream的方式更优雅.而且,结果也是正确的.但是,上面的代码,有没有迭代呢?有,但是,这里的迭代,不再与逻辑耦合,这个,就叫内部迭代.

stream 的特性

尽管stream的获取方式,离不开数组或者集合,但是,stream对数据的计算,与传统方式完全不同.
stream的特性,主要分为四个方面:
1.无存储 。Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,集合等。
如何理解视图呢?
比如,找出班上最高的五名同学,仅仅是计算全班的身高而已,而不会影响这五名同学.不会对整个班级有任何影响.

2.不修改 。对Stream的任何修改都不会修改背后的数据源.
比如过滤操作并不会删除被过滤的元素,而是产生一个新Stream。产生的心得Stream流,也仅仅是一个视图而已.

3.惰式执行。Stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
如果操作是中间操作的话,那么,是不会立马就执行的, 一直等到用户要做终端操作的时候,才会执行.
如果操作过程中,方法返回值类型是Stream的,那么他就是中间操作, 如果不是,就是终端操作.

List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream().filter(x -> {
    //经过测试,如果没有调用终端操作,该语句不会输出在控制台.
    System.out.println("过滤操作");
    return x > 1;
}).forEach(System.out::println);

filter方法,返回类型为Stream,所以,该方法就是中间操作.
foreach方法,返回类型不是Stream,所以,该方法就是终端操作.
惰式执行的意思是:上面的代码,如果我们不调用foreach方法(终端操作),那么filter方法(中间操作)是一定不会执行的,而在控制台上一定不会输出”过滤操作”.

4.可消费性。Stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

结论

总之:Stream是操作数据(主要解决的就是集合和数组)的一套工具,封装了对数据的各种操作:比如查找、过滤和映射(解决了原有的集合类的弊端).并且能够进行高效的大批量数据的操作.

猜你喜欢

转载自blog.csdn.net/wolfcode_cn/article/details/81359041