流初接触缓存
定义Dish类,它有两个属性,一个是名称name,另外一个是卡路里calories,而且有针对这两个属性的get和set方法,以下:app
; { ; ; () { ; } (calories) { .= calories; } () { ; } (name) { .= name; } }
接下来定义5个Dish对象,并把这些Dish对象放到集合menu中,以下:ide
<> = ArrayList<>(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add(); = Dish(); .setName(); .setCalories(); .add();
遍历menu集合,找出低卡路里的Dish对象,即找出卡路里小于400的Dish对象,但这些低卡路里对象须要放到另外容器中,以下:spa
<> = ArrayList<>(); (: ){ (.getCalories() < ){ .add(); } }
即又定义了一个名称叫lowCaloricDishes的List做为装低卡路里对象容器。
code
此时,咱们还想让lowCaloricDishes容器中的Dish对象按卡路里从低向高排序,那么按Java8以前的实现,一般会用匿名类进行排序:视频
.(, <>() { (o1, o2) { o1.getCalories() - o2.getCalories(); } });
最后还有点意犹未尽,想把低卡路里且按从低到高顺序排查后的Dish对象名称放到另外一个容器中对象
<> = ArrayList<>(); (: ){ .add(.getName()); }
在上面这点代码中就不断地使用中间容器List来建立缓存,当前Java8以前,你们都是这样写的,若是按Java8该如何修改呢?blog
List<String> lowCaloricDishesName = menu.stream().filter(d -> d.getCalories() < 400).sorted(Comparator.comparing(Dish::getCalories)).map(Dish::getName).collect(Collectors.toList());
其中filter(d -> d.getCalories() < 400)筛选出低卡路里的菜排序
sorted(comparing(Dish::getCalories))按卡路里从低向高排序get
map(Dish::getName)获取菜的名称
collect(toList())将名称保存在List中
书中将Java8这段代码叫声明性方式:只须要说明想要作啥,而不用说明如何实现。同时它把filter、sorted、map和collect称之为流水线,在上面的示例中,这几个流操做链是串行的。多盯下图几遍,了解一下流的概念:
2. 流与集合
集合能够理解为数据容器,里面装的都是数据; 流是计算,是对数据的计算。书中有一个例子,好比咱们在网上看视频,一个静态视频有许多帧(把帧当作数据),则这个视频就能够理解为集合,由于它装着不少帧数据; 当咱们在网上点击播放时,它并非把完整的视频下载下来后再播放(那是很早以前的年代那会作的事儿),而是一边播放一边下载,正在播放的这段缓存就是流。
这个问题比较好理解,就再也不啰嗦。
<> = .(, , ); .stream().forEach(.::println); .stream().forEach(.::println);
特别注意,下面两行是彻底同样的,先把集合转换成流,而后再遍历打印,它是正常的。
<> = .(, , ); <> = .stream(); .forEach(.::println); .forEach(.::println);
但这段代码就会抛出异常,由于流只能遍历一次,换言之,在遍历过程当中它被消费掉了。
这两个代码片断依旧以在线看电影为例来理解:
第一种是两次点播; 第二种是在一次点播中针对某段流尝试着两次放映。
3. 外部迭代和内部迭代
<> = ArrayList<>(); (: ){ (.getCalories() < ){ .add(); } }
这种就叫外部迭代,即咱们人为地使用代码进行遍历集合。固然,这段代码使用迭代器遍历,也是外部迭代,以下:
<> = .listIterator(); (.hasNext()){ = .next(); (.getCalories() < ){ .add(); } }
书上称之为丑陋的代码?啊,多么痛的打脸呀。
接下来,你告诉Lambda去遍历,而不用你本身去遍历,就叫内部迭代,以下:
menu.stream().filter(d -> d.getCalories() < 400);
你只须要去让Lambda去把低卡路里的Dish对象挑出来便可,至于它是怎么作的,你不用关心。
4. 流操做、中间操做和终端操做
能够把filter、map、limit和collect分别称为链,而这些链构成一个流,因此整个流水线就是一个流操做,而把对最后一个的操做称为End操做,中间的操做称为中间操做。这里看一下它们的类型:
menu List<String>
menu.stream(). stream<String>流 开始操做
filter(d -> d.getCalories() > 300). stream<String>流 中间操做
map(Dish::getName). stream<String>流 中间操做
limit(3). stream<String>流 中间操做
collect(toList()); List<string> 终端操做
为了了解中间到底经历了什么,咱们打印一下代码:
.stream().filter(d -> { ..println(+ d.getName()); d.getCalories() > ; }).map(d -> { ..println(+ d.getName()); d.getName(); }).limit().collect(.());
打印结果:
filtering d1 # 按条件过滤第1个元素,不符合直接进入下一个
filtering d2 #按条件过滤第2个元素,符合就获取名称:map name
mapping d2
filtering d3. #按条件过滤第3个元素,符合就获取名称:map name
mapping d3
filtering d4. #按条件过滤第4个元素,符合就获取名称:map name
mapping d4
终端操做:
终端操做会从流的流水线生成结果,其结果是任何不是流的值,好比List,Integer,甚至是void,但它不是stream