当你的Stream遇上Lambda就爱上了,超级无敌酷酷 - 第418篇
历史文章(累计400+篇文章)
《国内最全的Spring Boot系列之一》
《国内最全的Spring Boot系列之二》
《国内最全的Spring Boot系列之三》
《国内最全的Spring Boot系列之四》
《国内最全的Spring Boot系列之五》
你真的学会了Lambda表达式了吗?一篇让你学废了不香么 - 第417篇
悟纤:师傅上一节,你说要教我Stream来着,真的吗?
师傅:徒儿,你自己先学习过了没有?
悟纤:看过一些文章了,但是看完还是有点不知所以然。
师傅:Stream说简单也简单,说复杂也复杂。为师今天就来和你细说一下。
悟纤:好期待师傅的讲解哦~
导读:
在Spring Boot的框架的源码中会看到大量的Lambda和Stream,如果对于这两个特性不了解的话,那么看源码可能在某些代码上可能会看晕了,因此我们有必要来学习一下这两个特性。
Java8的两大大主要新特性Lambda表达式和Stream,两者提供了更高层次的抽象,简化开发,提高生产效率。
在前面的章节中,我们介绍了Lambda的概念以及详细的使用,这一节我们主要来看下Stream,当然在学习Stream中也会使用到Lambda。
一、Stream流
1.1 Stream简介
Stream是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
在思路上,类似于SQL的存储过程,有几个步骤:
(1)先定义一些操作的集合,注意:这里只定义,不真正执行
(2)触发执行,获取结果
(3)对结果进一步处理,筛选、打印、使用
其中,第1步的定义操作叫惰性求值,给你套路(返回Stream),但是不会执行返回结果。
第2步的触发操作叫及早求值,这个人说干就干,立马要结果(返回结果数据)。
第3步的筛选类似SQL的where子句,对结果进一步的筛选。
对于Stream你还要知道的:
(1)Stream 不是集合,自己不会存储元素。
(2)Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
(3)Stream 操作是延迟执行的。必须搞清楚有哪些数据才能往下执行,这意味着他们会等到需要结果的时候才执行。
(4)Stream只能“消费”一次,如果想继续做其他操作,需要重新获取stream对象。
(5)更像一个高级的iterator,单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,但是可以并行化数据!
1.2 Stream API
Stream 类位于java.util.stream包下,在这里提供了很多常用的方法就是API,我们说的Stream API,也就是这些方法的集合,比如:Stream.of(构建一个Stream对象),Stream.map(对象关系的映射)等等。
1.3 Stream的理解
大家可以把Stream当成一个装饰后的Iterator。原始版本的Iterator,用户只能逐个遍历元素并对其执行某些操作;包装后的Stream,用户只要给出需要对其包含的元素执行什么操作,比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,具体这些操作如何应用到每个元素上,就给Stream就好了!原先是人告诉计算机一步一步怎么做,现在是告诉计算机做什么,计算机自己决定怎么做。
1.4一个小栗子来理解Stream
我们看下这么一个需求,要统计集合中不为null的个数,在没有Stream的时候,我们会这么写代码:
List<Integer> nums = Arrays.asList(1,null,3,4,null,6);
//1.0版本 :使用for循环+count计数
long count = 0;
for(Integer num:nums){
if(num != null){
count++;
}
}
System.out.println("集合nums不为null的个数为:"+count);
使用Stream API的话,将会变得很简单:
List<Integer> nums = Arrays.asList(1,null,3,4,null,6);
// 2.0版本:使用Stream API
long count = nums.stream().filter(num -> num != null).count();
System.out.println("集合nums不为null的个数为:"+count);
上面这段代码是获取一个List中,元素不为null的个数。这段代码虽然很简短,但是却是一个很好的入门级别的例子来体现如何使用Stream,正所谓“麻雀虽小五脏俱全”。我们现在开始深入解刨这个例子,完成以后你可能可以基本掌握Stream的用法!
图片就是对于Stream例子的一个解析,可以很清楚的看见:原本一条语句被三种颜色的框分割成了三个部分:
(1)红色框中的语句是一个Stream的生命开始的地方,负责创建一个Stream实例;
(2)绿色框中的语句是赋予Stream灵魂的地方,把一个Stream转换成另外一个Stream,红框的语句生成的是一个包含所有nums变量的Stream,进过绿框的filter方法以后,重新生成了一个过滤掉原nums列表所有null以后的Stream;
(3)蓝色框中的语句是丰收的地方,把Stream的里面包含的内容按照某种算法来汇聚成一个值,例子中是获取Stream中包含的元素个数。
总的来说,使用Stream的常规的3大曲就是:
创建Stream·中间操作·终止操作。
一般都需要这3个步骤,看源码的时候,就按照这三个步骤进行阅读即可。
注意点就是:这里需要注意的是中间操作可以是一个链条(可能是0),也就是可是使用filter之后,然后再使用map。
二、Stream API使用
我们还是具体来看一些例子来理解Stream API的使用吧,看多了也就懂了。讲再多的知识点,不如动手来点实例说的清楚。
2.1 Stream.of(T… t)
要使用Stream,那就必须得先创建一个Stream,比如:
Stream<String> StrStream = Stream.of("a", "b","c");
当然这里是直接使用Stream的api创建的,在实际的代码中,我们的集合会有相应的方法进行构建Stream,比如List:
List<String> words = Arrays.asList(new String[]{"a","b","c"});
Stream<String> wordsStream = words.stream();
2.2 Stream.collect(Collector<?super T, A, R> collector)
使用收集器Collector将StrStream转化为熟悉的集合Collection:
Stream<String> StrStream = Stream.of("a", "b","c");
List<String> list = StrStream.collect(Collectors.toList());
//输出结果:[a, b, c]
System.out.println(list);
2.3 Stream.map(Function<? superT, ? extends R> mapper)
所谓map,从字面理解就是映射。这里指的是对象关系的映射,看下例子:
//从小写字母映射到大写字母:
List<String> words = Arrays.asList("a","b","c");
List<String> newWords = words.stream().map(word->word.toUpperCase() ).collect(Collectors.toList());
//输出结果:[A, B, C]
System.out.println(newWords);
2.4 Stream.filter(Predicate<? super T>predicate)
filter顾名思义,过滤筛选。这里的参数函数接口是一个条件,筛选出满足条件的元素:
//筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数,
List<String> languages = Arrays.asList("java","javascript","php","python");
long count = languages.stream().filter( str->str.startsWith("j") ).count();
//输出结果:2
System.out.println(count);
具体的还有很多方法,大家根据需要自行学习。
三、Stream API之Filter代码拆解
为了让大家对于Stream的这个使用有一个更深的理解,这里使用拆解的方式对于上面的一个例子“筛选出以 j 字母开头的元素个数”详细的说明一下:
3.1 需求说明
有这么一组数据"java","javascript","php","python":筛选出以 j 字母开头的元素个数。
3.2 1.0:一般的写法
一般的写法就是for遍历,然后进行统计:
//筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数,
List<String> languages = Arrays.asList("java","javascript","php","python");
long count = 0;
for(String str:languages){
if(str.startsWith("j")){
count++;
}
}
//输出结果:2
System.out.println(count);
3.3 2.0:Stream的写法
使用Stream可以获取流对象,然后对流进行操作,也就是Stream中提供的API:
//筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数,
List<String> languages = Arrays.asList("java","javascript","php","python");
Stream<String> oldStream = languages.stream();//1.构建流
Stream<String> newStream = oldStream.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("j");
}
});//2.中间操作
long count = newStream.count();//3.终止操作
//输出结果:2
System.out.println(count);
3.4 3.0:Stream+Lambda的写法
这里我们发现Predicate是一个接口,在此接口上注解了@FunctionalInterface,那么就支持Lambda的写法:
那么就可以对其进行优化:
//筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数,
List<String> languages = Arrays.asList("java","javascript","php","python");
Stream<String> oldStream = languages.stream();//1.构建流
Stream<String> newStream = oldStream.filter( s->s.startsWith("j") );//2.中间操作
long count = newStream.count();//3.终止操作
//输出结果:2
System.out.println(count);
在上面我们定义了很多的中间变量,其实没什么鸟用,直接合成一句代码即可:
//筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数,
List<String> languages = Arrays.asList("java","javascript","php","python");
long count = languages.stream()//1.构建流
.filter( s->s.startsWith("j") )//2.中间操作
.count();//3.终止操作
//输出结果:2
System.out.println(count);
这个乍一看不就是链式编程吗,只是加入了lambda的链接编程,让代码变得有点复杂了,如果是直接的set属性的方式,那么就很好理解了。
最后把代码写成一行,就是最终的结果了:
//筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数,
List<String> languages = Arrays.asList("java","javascript","php","python");
long count = languages.stream().filter( s->s.startsWith("j") ).count();
//输出结果:2
System.out.println(count);
结束语
相信通过这一节的讲解,对于Lambda和Stream有了一个比较深刻的理解,更多的还是需要自己去敲敲代码以及看更多的代码来加深自己对于这两个特性的认知。
我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。
à云课堂学院:悟空学院
学院中有Spring Boot相关的课程!!
SpringBoot视频:从零开始学Spring Boot Plus - 网易云课堂
SpringBoot交流平台:https://t.cn/R3QDhU0
SpringSecurity5.0视频:权限管理spring security - 网易云课堂
ShardingJDBC分库分表:分库分表Sharding-JDBC实战 - 网易云课堂
分布式事务解决方案:分布式事务解决方案「手写代码」 - 网易云课堂
JVM内存模型调优实战:深入理解JVM内存模型/调优实战 - 网易云课堂
Spring入门到精通:Spring零基础从入门到精通 - 网易云课堂
大话设计模式之爱你:大话设计模式之爱你一万年 - 网易云课堂
当你的Stream遇上Lambda就爱上了,超级无敌酷酷 - 第418篇相关推荐
- 当Bert遇上Keras:这可能是Bert最简单的打开姿势
作者丨苏剑林 研究方向丨NLP,神经网络 个人主页丨kexue.fm Bert 是什么,估计也不用笔者来诸多介绍了.虽然笔者不是很喜欢Bert,但不得不说,Bert 确实在 NLP 界引起了一阵轩然大 ...
- 文末送书 | 当Python遇上高考,会发生什么?
(文末送书哦!) 延期一个月之后,1071万考生终于熬出头了. 这届高考太难了,不仅考学生,更是考验疫情的防控能力. 但是说到难,2018年浙江省教育厅的一个决定,让不少人感叹真难! 原来早在2017 ...
- 当Python遇上高考,会发生什么?
延期一个月之后,今天,1071万考生终于走入考场. 这届高考太难了,不仅考学生,更是考验疫情的防控能力. 但是说到难,2018年浙江省教育厅的一个决定,让不少人感叹真难! 原来早在2017年底,就有消 ...
- 推荐系统遇上深度学习,9篇阿里推荐论文汇总!
作者 | 石晓文 转载自小小挖掘机(ID: wAIsjwj) 业界常用的推荐系统主要分为两个阶段,召回阶段和精排阶段,当然有时候在最后还会接一些打散或者探索的规则,这点咱们就不考虑了. 前面九篇文章中 ...
- 华为平板电脑_当5G遇上平板电脑,华为MatePad Pro 5G带来了什么?
5G已经来临,科技产品向5G升级已是大势所趋,这更是检验实力的探索之路. 2月24日,华为在巴塞罗那在线发布了一系列新品,其中,华为面向全球推出的5G高端旗舰平板,同时也是全球首款公开发布的5G平板华 ...
- Stream流与Lambda表达式(一) 杂谈
一.流 转换为数组.集合 package com.java.design.java8.Stream;import org.junit.Test; import org.junit.runner.Run ...
- 404未找到是什么意思_为什么老遇上404 not found?你懂的
文章转载自公众号:一只学霸(bajie203) 昨天晚上 大毛火急火燎地打开了电脑 戴上了耳机 不到两分钟 -- 我们往前一凑 登等 果然是大家最害怕的一幕出现了 学霸在网上冲浪多年 留下的都是美好的 ...
- 《当用户体验设计遇上敏捷》一3.5 小结
本节书摘来自异步社区<当用户体验设计遇上敏捷>一书中的第3章,第3.5节,作者[英]Lindsay Ratcliffe , Marc McNeill,更多章节内容可以访问云栖社区" ...
- 当网络安全遇上大数据分析(9)
2012年3月份,Gartner发表过一篇报告--Information Security Is Becoming a Big Data Analytics Problem .里面主要就讲到了针对大规 ...
最新文章
- 轻松搞定c++语言pdf_当年锤子的大爆炸,如今12个语言版本都可轻松搞定!
- 计算1到N的十进制数中1的出现次数
- 一架无人机加入警队4个月,墨西哥小城犯罪率下降了10%
- 疯狂连连看之开发界面布局
- asp.net跳转页面的三种方法比较
- java 的权限表_JAVA权限表
- Wilcoxon秩和检验
- Hive分区修复msck repair
- Android实战技巧之三十四:用TableLayout伪装表格显示数据
- String字符串类及有关内存分析
- 循环的数学应用————21.特殊等式 xyz+yzz =532
- Qunar 云原生容器化落地实践
- 一起学 WebGL:图元的类型
- Android StepsView 步骤控件
- CGroup的原理和使用
- html 表格自动排序,jQuery html表格排序插件tablesorter使用方法详解
- 将消息转发到客服+php,将消息转发到微信客服
- pandas函数melt的应用
- Docker构建Shipyard
- Jmeter性能测试指标