关于Stream在Java8中是占非常主要的地位的,所以这次对它进行进一步探讨【这次基本上都是偏理论的东东,但是理解它很重要~】,其实流跟咱们数据库学习当中的sql语句的特点是非常非常之像的,为什么这么说,下面以这个sql语句举例说明:
“select name from student where age > 20 and address = 'beijing' order by age desc;”
该简单的sql所要表达的意思是:从student这张表中查询出年龄>20并且地址=北京的记录,并且对年龄进行降序排序,排序之后将其名字查找出来。对于sql其实是一个描述性的语言,只描述其行为,而具体如何让db完成这个行为是没有暴露出来的,对于该sql所做的工作如果换成咱们的stream来实现那会是个什么样子呢,下面写一下伪代码:
首先从源数据中获得stream:
students.stream();
接着进行条件过滤:age > 20 and address = 'beijing',如下:
students.stream().filter(student -> student.getAge() > 20).filter(student -> student.getAddress().equals("beijing"));
然后对年纪进行排序,这里写伪代码:
students.stream().filter(student -> student.getAge() > 20).filter(student -> student.getAddress().equals("beijing")).sorted(...);
最后将其名字打印出来,如下:
students.stream().filter(student -> student.getAge() > 20).filter(student -> student.getAddress().equals("beijing")).sorted(...).forEach(student -> System.out.println(student.getName()));
是不是从表现形式上跟sql语句差不多,Stream也是属于一种描述性的语句, 整个语句并没有告诉底层Stream要如何去做,等于只要发一些指令给底层就可以了,具体底层怎么做完全不用关心。
其实对于这种Stream()的这种方式是一种内部迭待,而如果换成传统的方式既为外部迭待方式,如下:
List<Student> list = new ArrayList<>(); //首先对数据进行条件过滤 for(int i=0; i < students.size();i++){ Student student = students.get(i); if(student.getAge() > 20 && student.getAddress().equals("beijing")){ list.add(student); } } //接着对其按年龄排序 Collections.sort(list. Comparator()...); //将名字打印出来 for(Student student : list){ System.out.println(student.getName()); }
是不是发现跟使用Stream的方式完全没法相比,相差太多了,而且本质上也有区别,之所以上面传统的方式为外部迭待,很显然用到了一个中间的临时变量,另外将业务条件跟具体遍历代码都混在一起了,语义上理解也没这么容易,假如说对于一个不懂代码的人来看这两种实现方式,肯定是看具有描述性语句的stream的方式更加容易理解。
接下来用图的方式来阐述一下内部迭待与外部迭待的本质区别:
外部迭待:
针对一个集合:
以咱们这个例子为例,会使用for循环对其进行迭待,所以会单独在集合之外写入咱们自己的处理逻辑,如下:
其中集合与咱们自己编写的处理逻辑之间是有清晰的划分的:
内部迭待:
那对于stream的内部迭待方式表现又如何呢?
首先操作的对象就不是集合啦,而是集合的流,如下:
而接下来内的体现就来了,咱们自己写的代码和流会融合在一起了,而非自己写的这块代码相对于流是独立的:
合并到一起有什么好处呢?这样流就可以拿到咱们自己所提供的代码进行相应的优化,当遇到咱们调用的终止操作则将咱们提供的所有中间操作与流之前所提供好的基础功能进行统一的处理。
其实内部迭待有点像是做填充题一样,对于一篇写好的英文文章,空缺了几个填充选项【将某个单词或某个语句被扣出来了】,而咱们写的代码其实就是填补这些空缺的地方,而不是完全由自己发挥想象来写这篇英文文章;而传统的外部迭代方式则是给一个命题里面所有的内容都得由自己来编写。
另外内部迭待与外部迭待还有另外一个最大的区别,那就是内部迭待支持并行处理,而所有的并行处理其stream都已经代替咱们来处理了,也就是从调用者的角度来想就不用关心处理并行的问题,而传统的外部迭待一般都是串行的,如果也想要并行处理那这个并行的东东完全得自己来处理。
对于集合和流最后再来用一句话来概括:集合关注的是数据与数据存储本身;流关注的则是对数据的计算。而流与迭待器类似的一点是:流是无法重复使用或消费的。
另外对于流再来说明一下:对于流来说,中间操作都会返回一个Stream对象,而终止操作则不会返回Stream类型,它可能不返回值,也可能返回其它类型的单个值。所以说区分中间操作与终止操作的条件就是看返回的类型,如果是Stream对象一定是中间操作。