Java 8开始引入了Lambda表达式,第一次使用的时候,我是感动的,真的太好用了。接下来将做些笔记,以期了解Java函数式编程。
快速入手
流(Stream)是什么?这里有一篇文章(《Java 8中的Streams API详解》)给出了解释。这里先不纠结抽象的概念,而是看看具体的代码操作,本人认为,对一个Java程序员来说,Lambda表达式中的()和{}是需要区分的内容。
Lambda中() & {}
首先看一个Filter的例子:
|
|
Lambda是采用的 T -> Function -> R 的形式,string -> Character.isDigit(string.charAt(0))也可以添加()
写成string -> (Character.isDigit(string.charAt(0)))。
如果更换成{}
,则需要显式的写return语句,就像正常书写方法一样,打印下面的语句:
|
|
输出为:
|
|
这段输出中,一开始没有打印出”sdsdsss”,说明这不是一个执行过程,而更像是声明方法刻画Stream。
要执行”sdsdsss”,需要采用及早求值方法
(与之相反的概念叫惰性求值方法
,是上一句提到的“声明”和”刻画”的方法):
|
|
输出结果:
|
|
下图模拟了执行过程:
与树的遍历做比较:形式上,不那么严谨的说,很类似广度优先遍历,而一般的for循环更似深度优先遍历。
重构代码
本小节将采用前文所述方式重构一个多层for循环代码。
将要给出的实例中,for循环所处的场景和循环逻辑是这样的:现在某学校有5个班,id分别为1、2、3、4、5,每个班有三名学生,将所属班级id为奇数且名称不叫小明的学生的姓名,全部打印出来。代码如下:
|
|
代码各主要部分都进行了注释,逻辑也比较简单。
如何改造这段代码?我的方法是,视一次执行流程操作的对象对应一个Stream元素,首先看第一句for (Clazz c : clazzes)
,一次执行流程是的操作对象是c,那么该语句转化为:
|
|
第二句是if (c.getId() % 2 == 1)
,这里显然可以采用一个filter方法:
|
|
第三句是for (String s : c.getS())
,一次执行流程的操作对象是s,而s代表每个班级中的一个学生名,所以之前以班级对象对应一个Stream元素来构造Stream的方式,应该改为以班级中单个学生名对应一个Stream元素来构造Stream的方式。这里采用flatMap方法:
|
|
接着还要进行一次filter(),排除叫”小明”的名字,最后进行收集,这一部分完整代码如下:
|
|
输出为:
|
|
通过本次重构,代码逻辑清新了很多。
小结
本篇小文没有讲解map、reduce等高级函数如何使用,具体用法需自行查阅,但比较了lambda表达式中()与{}的区别,最后通过一个重构实例对函数式编程进行了实践。
参考
廖雪峰的官方网站:这里有对reduce函数的介绍,比较清晰;