主要内容
本篇主要是初步了解一下Stream是什么,有哪些种类,初步接触一下。
- 流是什么
- 流与集合
- 流操作种类
流是什么
流简介
流(Stream)是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个,有点像sql语句已经定义好了一部分可以使用函数,然后组合使用即可)。此外还可以透明地并行处理。
流简短的定义:从支持数据处理操作的源生成的元素序列。
- 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(如ArrayList 与 LinkedList)。但流的目的在于表达计算,比如你前面见到的filter、sorted和map。集合讲的是数据,流讲的是计算。
- 源 ——流会使用一个提供数据的源,入集合、数组和输入输出源。注意的是,从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序和列表一致。
- 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,入filter、map、reduce、find、match、sort等。流可以顺序处理也可以并行执行。
- 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,行程一个大的流水线。
- 内部迭代——与使用迭代器显示迭代的集合不同,流的迭代是在别后进行的。
下面看一个简单的例子:
//未使用流处理数据 List< Dish > lowCaloricDishes = new ArrayList< >(); //用匿名类对 菜肴排序 for(Dish d: menu){ Collections.sort(lowCaloricDishes, new Comparator< Dish >() { public int compare(Dish d1, Dish d2){ return Integer.compare(d1.getCalories(), d2.getCalories()); } }; //使用Stream流来处理 List< String > lowCaloricDishesName = menu.stream() //产生流 .filter(d -> d.getCalories() < 400) //选出400卡路里一下的菜肴 .sorted(comparing(Dish::getCalories)) //按照卡路里进行排序 .map(Dish::getName) //得到菜的名字 .collect(toList()); //将所有的名称保存在List中
上面就是一个我们经常在开发中使用到的代码,现在我们来看一下上面代码的好处:
- 代码以声明性方式写的:说明想要完成什么,而不是说明如何实现一个操作。是代码简洁易读
- 可以把几个基础操作链接起来,来表达复杂的数据处理。
由此可以总结出 Java 8 中的Stream API可以让你写出这样的代码:
- 声明性:更简洁,更易读
- 可复合:更灵活
- 可并行:性能更好
流与集合
Java现有的集合概念和新的流概念都提供了接口,来配合代表元素型有序值的数据接口。所谓有序,就是说我们一般是按顺序取用值,而不是随机取用的。那这两者有什么区别呢?
我们先来打个直观的比方吧。比如说存在DVD里的电影,这就是一个集合(也许是字节,也许是帧,这个无所谓),因为它包含了整个数据结构。现在再来想想在互联网上通过视频流看同样的电影。现在这是一个流(字节流或帧流)。流媒体视频播放器只要提前下载用户观看位置的那几帧就可以了,这样不用等到流中大部分值计算出来,你就可以显示流的开始部分了(想想观看直播足球赛)。特别要注意,视频播放器可能没有将整个流作为集合,保存所需要的内存缓冲区——而且要是非得等到最后一帧出现才能开始看,那等待的时间就太长了。出于实现的考虑,你也可以让视频播放器把流的一部分缓存在集合里,但和概念上的差异不是一回事。
粗略地说,集合与流之间的差异就在于什么时候进行计算。集合是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中。(你可以往集合里加东西或者删东西,但是不管什么时候,集合中的每个元素都是放在内存里的,元素都得先算出来才能成为集合的一部分。)相比之下,流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。 这对编程有很大的好处。这个思想就是用户仅仅从流中提取需要的值,而这些值——在用户看不见的地方——只会按需生成。这是一种生产者-消费者的关系。从另一个角度来说,流就像是一个延迟创建的集合:只有在消费者要求的时候才会计算值(用管理学的话说这就是需求驱动,甚至是实时制造)。
与此相反,集合则是急切创建的(供应商驱动:先把仓库装满,再开始卖,就像那些昙花一现的圣诞新玩意儿一样)。以质数为例,要是想创建一个包含所有质数的集合,那这个程序算起来就没完没了了,因为总有新的质数要算,然后把它加到集合里面。当然这个集合是永远也创建不完的,消费者这辈子都见不着了。
下图就是用DVD对比在线流媒体的例子展示了流和集合之间的差异。
另一个例子是用浏览器进行互联网搜索。假设你搜索的短语在Google或是网店里面有很多匹
配项。你用不着等到所有结果和照片的集合下载完,而是得到一个流,里面有最好的10个或20 3
个匹配项,还有一个按钮来查看下面10个或20个。当你作为消费者点击“下面10个”的时候,供
应商就按需计算这些结果,然后再送回你的浏览器上显示。集合与流的区别
- 流只能遍历一次,遍历完之后就说这个流已经被消费掉了。这与集合不同,集合可以多次使用。
- 流是内部迭代,集合是外部迭代。也就是集合需要自己写代码利用Iterator来进行遍历,而流是已经帮我们写好了迭代代码,我们只要用就可以。
流操作种类
流操作有俩类操作
- 中间操作:诸如filter或sorted操作会返回一个流。而这类操作定义的时候不会立即执行,而是等到遇见终端操作才会执行。
- 终端操作:从流的流水线生成结果。其结果是任何不是流的值,比如List,Integer。