Java8流

  1. 流初接触缓存

定义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称之为流水线,在上面的示例中,这几个流操做链是串行的。多盯下图几遍,了解一下流的概念:

image.png

image.png

image.png

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. 流操做、中间操做和终端操做

image.png

能够把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

相关文章
相关标签/搜索