概述
使用了 Java8
的 Stream API
之后,在这里写下自己的理解,督促自己,也分享给大家
打印问题
看下代码, 问:
- 代码能编译通过吗?
- 如果可以编译通过,执行一下代码,在
done
之前会输出什么东西?
public static void main(String[] args) {
List<String> list = Arrays.asList("123", "abc", "123", "456");
list.stream()
.filter("123"::equals)
.filter(k -> {
System.out.println(k);
return true;
});
System.out.println("done");
}
自问自答
我来分别回答一下上面提的问题
问题1, 代码能编译通过吗?
回答是: 可以,filter
写完对象其实是一个 Stream<String>
对象
问题2, 在 done 之前输出什么?
回答是: 什么都不会输出
有的同学可能会回答是,输出了两个 123
这么理解是不对的,第二个 filter
里面的代码至始至终都没有被运行过
要理解这个问题,看下我的分析和理解
Stream
如果把 Stream
比喻为 洗手间的感应式水龙头管道
, 这样大家可能比较好理解
看图
![](https://img.haomeiwen.com/i13864094/b234a01d5ea1b81b.png)
我们在 stream
后面,
加 filter
的时候,相当于在给管道加过滤网, 只有符合条件的水,才能继续往下流
加map
的时候,相当于再给管道加转换器, 如把冷水
加热变为 热水
, 自来水
净化成 可饮用水
那么思考一下问题2
为什么不会输出?
因为 Stream
有个特性就是 惰性求值
也可以理解为 没有终止操作的时候,流的中间过程是不会执行的
即: 没有订阅者的时候,流不会执行
即: 水龙头下面没有手的时候,水龙头是不会出水的
惰性求值
惰性求值,在Stream
的很多地方都体现出来了
比如 Spring 5
的 Flux
or Mono
不添加订阅者(或者称之为消费者)的时候,代码不会真正的去运行
Java 9
里的 Flow
亦是如此
循环次数
我们把代码改一改,增加一个什么都不做的消费者
public static void main(String[] args) {
List<String> list = Arrays.asList("123", "abc", "123", "456");
list.stream()
.filter("123"::equals)
.filter(k -> {
System.out.println(k);
return true;
})
.forEach(k -> {});
;
System.out.println("done");
}
再提出一个问题:对于代码里的 第二个filter,它收到的数据是什么样的?
或者换个说法,我们知道第二个filter 处理的数据是 [123 123]
但是这两个数据是一起过来的,还是分批来的?
借用上面的例子 洗手间的感应式水龙头管道
假设把水分为5份,那么逻辑是不是这样的:
第一个filter拿到5份水之后,遍历,过滤,剩下两份水,
第二个filter拿到两份水,遍历,过滤,剩下两份水(因为return true
)
还是说是别的场景?
其实流程不是这样的,看源码得知:
水分为5份,编号为 水1、水2。。。水5
水1
先尝试流经 Stream
的这整个管道(如果能通过的话)
接着是水2
最后是水5
结论是: 至始至终,只循环了一次
网友评论