`
deepinmind
  • 浏览: 446133 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:40988
社区版块
存档分类
最新评论

Java函数式编程(五)列表的转化

阅读更多
本系列文章译自Venkat Subramaniam的Functional Programming in Java

列表的转化

将集合转化成一个新的集合就和遍历它一样简单。假设我们要将列表中的名字转化成全大写的。我们看下都有哪些实现方式。

Java中的字符串是不可变的,所以它没法改变。我们可以生成新的字符串,用来替换列表中原有的元素。然而这样做的话,原来列表就没了;还有一个问题,原来的列表可能也是不可变的,比如Arrays.asList()生成的,所以修改原来的列表这招不行。还有一个缺点就是这样做很难并行操作。

生成一个新的全大写的列表是个不错的选择。

乍听起来这个建议弱爆了;性能是我们都很关注的一个问题。令人吃惊的是,函数式编程通常要比命令式的性能要高,我们在153页的性能问题中会讲到。

我们先开始用这个集合生成一个大写字母的新集合吧。

final List<String> uppercaseNames = new ArrayList<String>();
for(String name : friends) {
uppercaseNames.add(name.toUpperCase());
}


在命令式的代码中,我们先创建一个空列表,然后把大写的名字填充进去,在遍历原来列表的过程中,每次插入一个。为了改进成函数式的版本,我们第一步可以考虑采用19页遍历列表中提到的那个内部迭代器forEach来替换一下for循环,正如下例所示的那样。

final List<String> uppercaseNames = new ArrayList<String>();
friends.forEach(name -> uppercaseNames.add(name.toUpperCase()));
System.out.println(uppercaseNames);


我们用了内部迭代器,但还得新建一个列表,然后再把元素插入到里面。我们还可以进一步改进。

使用lambda表达式

一个新引入的Stream接口里面,有个map方法,它可以帮助我们远离可变性,并使代码看起来更简洁。Steam有点像集合的迭代器,同时它还提供了流函数(fluent functions)的功能。使用这个接口的方法,我们可以把一系列调用给组合起来,使代码读起来就像描述问题的顺序一样,可读性更强。

Steam的map方法可以用来将输入序列转化成一个输出的序列——这和我们要做的工作非常匹配。

friends.stream()
.map(name -> name.toUpperCase())
.forEach(name -> System.out.print(name + " "));
System.out.println();


JDK8中的所有集合都支持这个stream方法,它把集合封装成一个Steam实例。map方法对Stream中的每个元素都调用了指定的lambda表达式或者代码块。map方法跟forEach方法很不一样, forEach只是简单的对集合中的元素执行了一下指定的函数。而map方法把lambda表达式的运行结果收齐起来,返回一个结果集。最后我们用forEach方法打印了所有的元素。

新集合中的名字全都是大写的了:

BRIAN NATE NEAL RAJU SARA SCOTT


map方法很适合把一个输入集合转化成一个新的输出集合。这个方法确保了输入输出序列的元素的数量是相同的。然而输入元素和输出元素的类型可以是不一样的。在这个例子中,我们输入和输出的都是字符串的集合。我们可以传给map方法一段代码,让它返回比如说名字中包含字符的个数。这样的话,输入的还是字符串的序列,而输出的却是数字序列了,就像下面这样。

friends.stream()
.map(name -> name.length())
.forEach(count -> System.out.print(count + " "));


结果是每个名字中字母的个数:
5 4 4 4 4 5


使用了lambda表达式的之后版本,避免了显式的修改操作;这样的代码非常简洁。这样写不再需要初始化空的集合以及那个垃圾变量了;这个变量乖乖的躲到了底层实现里面了。

使用方法引用

我们还可以使用方法引用让它变得更简洁一些。在需要传入函数式接口的实现的地方,Java编译器可以接受lambda表达式或者是方法引用。有了这个特性,用String::toUpperCase就可以替换掉name -> name.toUpperCase()了,就像这样:

friends.stream()
.map(String::toUpperCase)
.forEach(name -> System.out.println(name));


当参数传入到这个生成的方法——函数式接口的抽象方法的实现——里面的时候,Java会去调用这个String参数的toUpperCase方法。这个参数引用在这里就隐藏起来了。像前面这种简单的场景,我们可以用方法引用来替换掉lambda表达式;更多的内容看一下26页的什么时候应该使用方法引用


<blockquote>
小伙伴发问了:
<br>
什么时候应该使用方法引用?
<br>
<br>

当使用Java编程的时候,通常我们用lambda表达式的时候要比方法引用多得多。但这并不意味着方法引用不重要或者没啥用处。当lambda表达式非常简短的时候,它是一个很好的替代方案,它直接调用了实例方法或者静态方法。也就是说,如果lambda表达式只是传递了一下参数给方法调用的话,我们应该改用方法引用。
<br>

像这样的lambda表达式,有点像Tom Smykowski在电影上班一条虫中讲的那样,它的工作就是"从客户那把需求拿给软件工程师"。因为这个,我把这种重构成方法引用的模式叫做上班一条虫模式。
<br>

除了简洁外,使用方法引用,方法名字本身的含义和作用可以更好的体现出来。
<br>

使用方法引用背后,编译器起到了很关键的作用。方法引用的目标对象和参数都会从这个生成的方法里传进来的参数那推导出来。这才使得你可以使用方法引用写出比使用lambda表达式更简洁的代码。不过,如果参数在传递给方法之前或者调用结果在返回之后要被修改的话,这种便利的写法我们就用不了了。

</blockquote>

在前面这个例子中,方法引用是引用了一个实例方法。方法引用还可以引用一个静态方法以及接受传参的方法。后面我们会看到这样的例子。



lambda表达式能帮助我们遍历集合,并且进行集合的转化。就像下面我们即将看到的,它还能帮助我们快速的从集合中选取一个元素。


未完待续,后续文章请继续关注deepinmind


原创文章转载请注明出处:http://it.deepinmind.com







3
6
分享到:
评论

相关推荐

    Scala函数式编程

    函数式编程(FP)是一种软件开发风格,它注重不依赖于编程状态的函数。函数式代码易于测试和复用,容易实现并发,且不容易受到bug的攻击。Scala是一种能很好支持函数式编程的新兴JVM语言。《Scala函数式编程》是针对...

    Java函数式编程(三):列表的转化

    主要介绍了Java函数式编程(二):列表的转化,lambda表达式不仅能帮助我们遍历集合,并且可以进行集合的转化,需要的朋友可以参考下

    java面试题

    84.8. 将一个键盘输入的数字转化成中文输出(例如:输入1234567,输出:一百二拾三万四千五百六拾七),请用java语言编一段程序实现! 114 84.9. 题目1:用1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有...

    Scala程序设计(第2版)

    6.7 函数式编程的数据结构 162 6.7.1 序列 162 6.7.2 映射表 166 6.7.3 集合 168 6.8 遍历、映射、过滤、折叠与归约 168 6.8.1 遍历 169 6.8.2 映射 170 6.8.3 扁平映射 172 6.8.4 过滤 ...

    新版Android开发教程.rar

    ----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 开放手机联盟 --Open --Open --Open --Open Handset Handset Handset Handset Alliance ...

    Python实战手册_CodingPark编程公园

    文章介绍 本文主要讲述 Python的代码实战,主要包括: ...列表解析式 字典解析式 Python交互 while循环 函数 类 对象 类属性修改 继承 python和java对应关系 原创文章 28获赞 18访问量 2222 关注

    JavaScript基础和实例代码

    1.11 JavaScript与Java、Java applet 1.12 JavaScript的未来如何 1.13 本章小结 第2章 JavaScript语言入门 2.1 编程准备 2.1.1 编程术语 2.1.2 脚本执行顺序 2.1.3 大小写敏感 2.1.4 空白字符 2.1.5 分号 2.1.6 块 ...

    源文件程序天下JAVASCRIPT实例自学手册

    1.11 JavaScript与Java、Java applet 1.12 JavaScript的未来如何 1.13 本章小结 第2章 JavaScript语言入门 2.1 编程准备 2.1.1 编程术语 2.1.2 脚本执行顺序 2.1.3 大小写敏感 2.1.4 空白字符 2.1.5 分号 2.1.6 块 ...

    八数码实验报告

    实验的主要目的通过实验加深对搜索策略的理解,尤其是对启发式搜索的基本原理的理解,使学生能够通过编程实现图搜索的基本方法和启发式搜索算法,并能够解决一些应用问题。 八数码问题是人工智能当中的一个著名难题...

    数据库课程设计设计指导书-最新.pdf

    根据个人能力,可以选用 VB、VB、C#、Java 等高级 编程语言设计界面(如果能够确认该内容为学生自做,可在程序设计部分获得加分,否则不 能加分) 。 六.课程设计进度安排(时间为两周,10 天) 时间 内容 学生上机...

    Spring API

    9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. IBM WebSphere 9.8.2. BEA WebLogic 9.8.3. Oracle OC4J 9.9. 常见问题的解决方法 9.9.1. 对一个特定的 DataSource 使用了错误...

    Spring中文帮助文档

    9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. IBM WebSphere 9.8.2. BEA WebLogic 9.8.3. Oracle OC4J 9.9. 常见问题的解决方法 9.9.1. 对一个特定的 DataSource 使用了错误...

    asp.net知识库

    多样式星期名字转换 [Design, C#] .NET关于string转换的一个小Bug Regular Expressions 完整的在.net后台执行javascript脚本集合 ASP.NET 中的正则表达式 常用的匹配正则表达式和实例 经典正则表达式 delegate vs. ...

    算法导论(part1)

    书中的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书中,作者将算法的讨论集中在一些比较现代的例子上,它们来自分子生物学(如...

    算法导论(part2)

    书中的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书中,作者将算法的讨论集中在一些比较现代的例子上,它们来自分子生物学(如...

    C#微软培训资料

    10.3 构造函数和析构函数 .119 10.4 小 结 .122 第十一章 方 法 .124 11.1 方法的声明.124 11.2 方法中的参数.125 11.3 静态和非静态的方法.129 11.4 方法的重载.130 11.5 操作符重载.134 11.6 小 ...

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    4.3.1根据适口属性设计响应式布局: 26 4.3.2同分辨率范围内的流式布局设计 26 4.3.3移动端viewport属性设定 27 4.3.4组件样式的渐进增强设计 27 4.4 本章小结 27 第五章 认我测在线检测服务系统设计 29 5.1认我测...

Global site tag (gtag.js) - Google Analytics