guava函数式编程
[Google Guava] 4-函数式编程
注意事项
截至JDK7,Java中也只能通过笨拙冗长的匿名类来达到近似函数式编程的效果。预计JDK8中会有所改变,但Guava现在就想给JDK5以上用户提供这类支持。
过度使用Guava函数式编程会导致冗长、混乱、可读性差而且低效的代码。这是迄今为止最容易(也是最经常)被滥用的部分,如果你想通过函数式风格达成一行代码,致使这行代码长到荒唐,Guava团队会泪流满面。
比较如下代码:
01 |
Function<String, Integer> lengthFunction = new Function<String, Integer>() { |
02 |
public Integer apply(String string) { |
03 |
return string.length(); |
04 |
} |
05 |
}; |
06 |
Predicate<String> allCaps = new Predicate<String>() { |
07 |
public boolean apply(String string) { |
08 |
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string); |
09 |
} |
10 |
}; |
11 |
Multiset<Integer> lengths = HashMultiset.create( |
12 |
Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction)); |
或FluentIterable的版本
01 |
Multiset<Integer> lengths = HashMultiset.create( |
02 |
FluentIterable.from(strings) |
03 |
.filter(new Predicate<String>() { |
04 |
public boolean apply(String string) { |
05 |
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string); |
06 |
} |
07 |
}) |
08 |
.transform(new Function<String, Integer>() { |
09 |
public Integer apply(String string) { |
10 |
return string.length(); |
11 |
} |
12 |
})); |
还有
1 |
Multiset<Integer> lengths = HashMultiset.create(); |
2 |
for (String string : strings) { |
3 |
if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) { |
4 |
lengths.add(string.length()); |
5 |
} |
6 |
} |
即使用了静态导入,甚至把Function和Predicate的声明放到别的文件,第一种代码实现仍然不简洁,可读性差并且效率较低。
截至JDK7,命令式代码仍应是默认和第一选择。不应该随便使用函数式风格,除非你绝对确定以下两点之一:
- 使用函数式风格以后,整个工程的代码行会净减少。在上面的例子中,函数式版本用了11行, 命令式代码用了6行,把函数的定义放到另一个文件或常量中,并不能帮助减少总代码行。
- 为了提高效率,转换集合的结果需要懒视图,而不是明确计算过的集合。此外,确保你已经阅读和重读了Effective Java的第55条,并且除了阅读本章后面的说明,你还真正做了性能测试并且有测试数据来证明函数式版本更快。
请务必确保,当使用Guava函数式的时候,用传统的命令式做同样的事情不会更具可读性。尝试把代码写下来,看看它是不是真的那么糟糕?会不会比你想尝试的极其笨拙的函数式 更具可读性。
Functions[函数]和Predicates[断言]
本节只讨论直接与Function和Predicate打交道的Guava功能。一些其他工具类也和”函数式风格”相关,例如Iterables.concat(Iterable<Iterable>),和其他用常量时间返回视图的方法。尝试看看2.3节的集合工具类。
Guava提供两个基本的函数式接口:
- Function<A, B>,它声明了单个方法B apply(A input)。Function对象通常被预期为引用透明的——没有副作用——并且引用透明性中的”相等”语义与equals一致,如a.equals(b)意味着function.apply(a).equals(function.apply(b))。
- Predicate<T>,它声明了单个方法boolean apply(T input)。Predicate对象通常也被预期为无副作用函数,并且”相等”语义与equals一致。
特殊的断言
字符类型有自己特定版本的Predicate——CharMatcher,它通常更高效,并且在某些需求方面更有用。CharMatcher实现了Predicate<Character>,可以当作Predicate一样使用,要把Predicate转成CharMatcher,可以使用CharMatcher.forPredicate。更多细节请参考第6章-字符串处理。
此外,对可比较类型和基于比较逻辑的Predicate,Range类可以满足大多数需求——它表示一个不可变区间。Range类实现了Predicate,用以判断值是否在区间内。例如,Range.atMost(2)就是个完全合法的Predicate<Integer>。更多使用Range的细节请参照第8章。
操作Functions和Predicates
Functions提供简便的Function构造和操作方法,包括:
| forMap(Map<A, B>) | compose(Function<B, C>, Function<A, B>) | constant(T) |
| identity() | toStringFunction() |
细节请参考Javadoc。
相应地,Predicates提供了更多构造和处理Predicate的方法,下面是一些例子:
细节请参考Javadoc。
使用函数式编程
Guava提供了很多工具方法,以便用Function或Predicate操作集合。这些方法通常可以在集合工具类找到,如Iterables,Lists,Sets,Maps,Multimaps等。
断言
断言的最基本应用就是过滤集合。所有Guava过滤方法都返回”视图”——译者注:即并非用一个新的集合表示过滤,而只是基于原集合的视图。
*List的过滤视图被省略了,因为不能有效地支持类似get(int)的操作。请改用Lists.newArrayList(Collections2.filter(list, predicate))做拷贝过滤。
除了简单过滤,Guava另外提供了若干用Predicate处理Iterable的工具——通常在Iterables工具类中,或者是FluentIterable的”fluent”(链式调用)方法。
| Iterables方法签名 | 说明 | 另请参见 |
| boolean all(Iterable, Predicate) | 是否所有元素满足断言?懒实现:如果发现有元素不满足,不会继续迭代 | Iterators.all(Iterator, Predicate)FluentIterable.allMatch(Predicate) |
| boolean any(Iterable, Predicate) | 是否有任意元素满足元素满足断言?懒实现:只会迭代到发现满足的元素 | Iterators.any(Iterator, Predicate)FluentIterable.anyMatch(Predicate) |
| T find(Iterable, Predicate) | 循环并返回一个满足元素满足断言的元素,如果没有则抛出NoSuchElementException | Iterators.find(Iterator, Predicate) Iterables.find(Iterable, Predicate, T default) Iterators.find(Iterator, Predicate, T default) |
| Optional<T> tryFind(Iterable, Predicate) | 返回一个满足元素满足断言的元素,若没有则返回Optional.absent() | Iterators.find(Iterator, Predicate) Iterables.find(Iterable, Predicate, T default) Iterators.find(Iterator, Predicate, T default) |
| indexOf(Iterable, Predicate) | 返回第一个满足元素满足断言的元素索引值,若没有返回-1 | Iterators.indexOf(Iterator, Predicate) |
| removeIf(Iterable, Predicate) | 移除所有满足元素满足断言的元素,实际调用Iterator.remove()方法 | Iterators.removeIf(Iterator, Predicate) |
函数
到目前为止,函数最常见的用途为转换集合。同样,所有的Guava转换方法也返回原集合的视图。
*Map和Multimap有特殊的方法,其中有个EntryTransformer<K, V1, V2>参数,它可以使用旧的键值来计算,并且用计算结果替换旧值。
*对Set的转换操作被省略了,因为不能有效支持contains(Object)操作——译者注:懒视图实际上不会全部计算转换后的Set元素,因此不能高效地支持contains(Object)。请改用Sets.newHashSet(Collections2.transform(set, function))进行拷贝转换。
01 |
List<String> names; |
02 |
Map<String, Person> personWithName; |
03 |
List<Person> people = Lists.transform(names, Functions.forMap(personWithName)); |
04 |
05 |
ListMultimap<String, String> firstNameToLastNames; |
06 |
// maps first names to all last names of people with that first name |
07 |
08 |
ListMultimap<String, String> firstNameToName = Multimaps.transformEntries(firstNameToLastNames, |
09 |
new EntryTransformer<String, String, String> () { |
10 |
public String transformEntry(String firstName, String lastName) { |
11 |
return firstName + " " + lastName; |
12 |
} |
13 |
}); |
可以组合Function使用的类包括:
| Ordering | Ordering.onResultOf(Function) |
| Predicate | Predicates.compose(Predicate, Function) |
| Equivalence | Equivalence.onResultOf(Function) |
| Supplier | Suppliers.compose(Function, Supplier) |
| Function | Functions.compose(Function, Function) |
此外,ListenableFuture API支持转换ListenableFuture。Futures也提供了接受AsyncFunction参数的方法。AsyncFunction是Function的变种,它允许异步计算值。
原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: [Google Guava] 4-函数式编程
沈义扬
Latest posts by 沈义扬 (see all)
- Google Guava官方教程(中文版) - 2014年4月11日
- [Google Guava] 排序: Guava强大的”流畅风格比较器” - 2014年4月11日
- [Google Guava] 12-数学运算 - 2014年4月9日
Related posts:
- [Google Guava] 2.3-强大的集合工具类:java.util.Collections中未包含的集合工具
- [Google Guava] 2.2-新集合类型
- [Google Guava] 2.4-集合扩展工具类
- [Google Guava] 排序: Guava强大的”流畅风格比较器”
- [Google Guava] 2.1-不可变集合
- [Google Guava] 7-原生类型
- [Google Guava] 8-区间
- [Google Guava] 3-缓存
- [Google Guava] 6-字符串处理:分割,连接,填充
- [Google Guava] 10-散列
- Google-Guava Concurrent包里的Service框架浅析
- [Google Guava] 1.3-常见Object方法
- [Google Guava] 9-I/O
- [Google Guava] 1.1-使用和避免null
- [Google Guava] 1.5-Throwables:简化异常和错误的传播与检查
guava函数式编程的更多相关文章
- Guava 是个风火轮之函数式编程(3)——表处理
云栖社区> 博客列表> 正文 Guava 是个风火轮之函数式编程(3)--表处理 潘家邦 2016-01-26 13:19:21 浏览1062 评论0 java Guava 摘要: 早先学 ...
- Java经典类库-Guava中的函数式编程讲解
如果我要新建一个java的项目,那么有两个类库是必备的,一个是junit,另一个是Guava.选择junit,因为我喜欢TDD,喜欢自动化测试.而是用Guava,是因为我喜欢简洁的API.Guava提 ...
- guava function and predicate 函数式编程
@Test public void function(){ List<String> list = Lists.newArrayList("1","2&quo ...
- 使用Guava进行函数式编程
本文翻译自Getting Started with Google Guava这本书,如有翻译不足的地方请指出. 在这一章,我们开始注意到使用Guava进行编写代码会更加简单.我们将看看如何使用Guav ...
- Guava 教程(3):Java 的函数式编程,通过 Google Collections 过滤和调用
原文出处: oschina 在本系列博客的第一.二部分,我介绍了非常优秀的Google Collections和Guava包.本篇博客中我们来看看如何使用Google Collections来做到过滤 ...
- JDK 8 函数式编程入门
目录 1. 概述 1.1 函数式编程简介 1.2 Lambda 表达式简介 2. Lambda 表达式 2.1 Lambda 表达式的形式 2.2 闭包 2.3 函数接口 3. 集合处理 3.1 St ...
- java8函数式编程(转载)
1. 概述 1.1 函数式编程简介 我们最常用的面向对象编程(Java)属于命令式编程(Imperative Programming)这种编程范式.常见的编程范式还有逻辑式编程(Logic Progr ...
- angular2系列教程(六)两种pipe:函数式编程与面向对象编程
今天,我们要讲的是angualr2的pipe这个知识点. 例子
- [学习笔记]JavaScript之函数式编程
欢迎指导与讨论:) 前言 函数式编程能使我们的代码结构变得简洁,让代码更接近于自然语言,易于理解. 一.减少不必要的函数嵌套代码 (1)当存在函数嵌套时,若内层函数的参数与外层函数的参数一致时,可以这 ...
随机推荐
- Quartz.NET---任务调度框架
在我们的程序中,可能经常会遇到"每隔多久执行XXX任务"这样的问题:每天晚上24:00审核用户提交的申请:每隔1分钟去数据库中检索用户是否有新的消息:... ...那么Quar ...
- HTML <a> 标签
一,定义和用法 所有浏览器都支持 <a> 标签. <a> 标签定义超链接,用于从一张页面链接到另一张页面. <a> 元素最重要的属性是 href 属性,它指示链接的 ...
- 读jQuery源码 - Deferred
Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax ...
- 转 用C API 操作MySQL数据库
用C API 操作MySQL数据库 参考MYSQL的帮助文档整理 这里归纳了C API可使用的函数,并在下一节详细介绍了它们.请参见25.2.3节,“C API函数描述”. 函数 描述 mysql_a ...
- Mac电脑清理硬盘"其他"
作为一个MacBook的使用者,无不感受到苹果对于系统和硬件的完美匹配. 苹果电脑不适合玩游戏,所以我只用它开发iOS使用.电脑里除了Xcode和常用办公软件与通讯软件以外,我没有装其他的任何大应用. ...
- android fragment 的用法以及与activity的交互和保存数据的方法,包括屏幕切换(转载)!
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017 1.管理Fragment回退栈 类似与Android系统为Acti ...
- Android 常用抓包工具介绍之Charles
➠更多技术干货请戳:听云博客 Charles是一款抓包修改工具,相比起TcpDump,charles具有界面简单直观,易于上手,数据请求控制容易,修改简单,抓取数据的开始暂停方便等等优势!前面介绍了如 ...
- C#之 Lambda表达式
Lambda表达式 简化了匿名委托的使用,让你让代码更加简洁,优雅.据说它是微软自c#1.0后新增的最重要的功能之一. 首先来看一下其发展 根据上面的发展历程,可以感到Lambda表达式愈加简化. 详 ...
- js document.createElement()的用法 (转)
document.createElement()的用法 分析代码时,发现自己的盲点--document.createElement(),冲浪一番,总结了点经验. document.createElem ...
- 【转】JAVA 8 日期/时间(Date Time)API指南
前言 本来想写下Java 8的日期/时间API,发现已经有篇不错的文章了,那就直接转载吧~ PS:主要内容没变,做了部分修改. 原文链接: journaldev 翻译: ImportNew.com - ...

