原文连接 译文连接 译者:沈义扬,校对:丁一javascript
注意事项
截至JDK7,Java中也只能经过笨拙冗长的匿名类来达到近似函数式编程的效果。预计JDK8中会有所改变,但Guava如今就想给JDK5以上用户提供这类支持。html
过分使用Guava函数式编程会致使冗长、混乱、可读性差并且低效的代码。这是迄今为止最容易(也是最常常)被滥用的部分,若是你想经过函数式风格达成一行代码,导致这行代码长到荒唐,Guava团队会泪流满面。java
比较以下代码:git
01 |
Function<String, Integer> lengthFunction = new Function<String, Integer>() { |
02 |
public Integer apply(String string) { |
03 |
return string.length(); |
06 |
Predicate<String> allCaps = new Predicate<String>() { |
07 |
public boolean apply(String string) { |
08 |
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string); |
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); |
08 |
.transform( new Function<String, Integer>() { |
09 |
public Integer apply(String string) { |
10 |
return string.length(); |
还有缓存
1 |
Multiset<Integer> lengths = HashMultiset.create(); |
2 |
for (String string : strings) { |
3 |
if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) { |
4 |
lengths.add(string.length()); |
即便用了静态导入,甚至把Function和Predicate的声明放到别的文件,第一种代码实现仍然不简洁,可读性差而且效率较低。
截至JDK7,命令式代码仍应是默认和第一选择。不该该随便使用函数式风格,除非你绝对肯定如下两点之一:并发
- 使用函数式风格之后,整个工程的代码行会净减小。在上面的例子中,函数式版本用了11行, 命令式代码用了6行,把函数的定义放到另外一个文件或常量中,并不能帮助减小总代码行。
- 为了提升效率,转换集合的结果须要懒视图,而不是明确计算过的集合。此外,确保你已经阅读和重读了Effective Java的第55条,而且除了阅读本章后面的说明,你还真正作了性能测试而且有测试数据来证实函数式版本更快。
请务必确保,当使用Guava函数式的时候,用传统的命令式作一样的事情不会更具可读性。尝试把代码写下来,看看它是否是真的那么糟糕?会不会比你想尝试的极其笨拙的函数式 更具可读性。app
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构造和操做方法,包括:
细节请参考Javadoc。
相应地,Predicates提供了更多构造和处理Predicate的方法,下面是一些例子:
细节请参考Javadoc。
使用函数式编程
Guava提供了不少工具方法,以便用Function或Predicate操做集合。这些方法一般能够在集合工具类找到,如Iterables,Lists,Sets,Maps,Multimaps等。
断言
断言的最基本应用就是过滤集合。全部Guava过滤方法都返回”视图”——译者注:即并不是用一个新的集合表示过滤,而只是基于原集合的视图。
集合类型 |
过滤方法 |
Iterable |
Iterables.filter(Iterable, Predicate)FluentIterable.filter(Predicate) |
Iterator |
Iterators.filter(Iterator, Predicate) |
Collection |
Collections2.filter(Collection, Predicate) |
Set |
Sets.filter(Set, Predicate) |
SortedSet |
Sets.filter(SortedSet, Predicate) |
Map |
Maps.filterKeys(Map, Predicate)Maps.filterValues(Map, Predicate)Maps.filterEntries(Map, Predicate) |
SortedMap |
Maps.filterKeys(SortedMap, Predicate)Maps.filterValues(SortedMap, Predicate)Maps.filterEntries(SortedMap, Predicate) |
Multimap |
Multimaps.filterKeys(Multimap, Predicate)Multimaps.filterValues(Multimap, Predicate)Multimaps.filterEntries(Multimap, Predicate) |
*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转换方法也返回原集合的视图。
集合类型 |
转换方法 |
Iterable |
Iterables.transform(Iterable, Function)FluentIterable.transform(Function) |
Iterator |
Iterators.transform(Iterator, Function) |
Collection |
Collections2.transform(Collection, Function) |
List |
Lists.transform(List, Function) |
Map* |
Maps.transformValues(Map, Function)Maps.transformEntries(Map, EntryTransformer) |
SortedMap* |
Maps.transformValues(SortedMap, Function)Maps.transformEntries(SortedMap, EntryTransformer) |
Multimap* |
Multimaps.transformValues(Multimap, Function)Multimaps.transformEntries(Multimap, EntryTransformer) |
ListMultimap* |
Multimaps.transformValues(ListMultimap, Function)Multimaps.transformEntries(ListMultimap, EntryTransformer) |
Table |
Tables.transformValues(Table, Function) |
*Map和Multimap有特殊的方法,其中有个EntryTransformer<K, V1, V2>参数,它可使用旧的键值来计算,而且用计算结果替换旧值。
*对Set的转换操做被省略了,由于不能有效支持contains(Object)操做——译者注:懒视图实际上不会所有计算转换后的Set元素,所以不能高效地支持contains(Object)。请改用Sets.newHashSet(Collections2.transform(set, function))进行拷贝转换。
02 |
Map<String, Person> personWithName; |
03 |
List<Person> people = Lists.transform(names, Functions.forMap(personWithName)); |
05 |
ListMultimap<String, String> firstNameToLastNames; |
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; |
能够组合Function使用的类包括:
此外,ListenableFuture API支持转换ListenableFuture。Futures也提供了接受AsyncFunction参数的方法。AsyncFunction是Function的变种,它容许异步计算值。
本站的翻译主编。关注并发编程,面向对象设计,分布式系统。
Latest posts by 沈义扬 (see all)

添加本文到个人收藏