guava函数式编程

[Google Guava] 4-函数式编程

原文连接 译文连接 译者:沈义扬,校对:丁一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();
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函数式的时候,用传统的命令式作一样的事情不会更具可读性。尝试把代码写下来,看看它是否是真的那么糟糕?会不会比你想尝试的极其笨拙的函数式 更具可读性。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构造和操做方法,包括:

forMap(Map<A, B>) compose(Function<B, C>, Function<A, B>) constant(T)
identity() toStringFunction()

细节请参考Javadoc。

相应地,Predicates提供了更多构造和处理Predicate的方法,下面是一些例子:

instanceOf(Class) assignableFrom(Class) contains(Pattern)
in(Collection) isNull() alwaysFalse()
alwaysTrue() equalTo(Object) compose(Predicate, Function)
and(Predicate...) or(Predicate...) not(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))进行拷贝转换。

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的变种,它容许异步计算值。

Futures.transform(ListenableFuture, Function)
Futures.transform(ListenableFuture, Function, Executor)
Futures.transform(ListenableFuture, AsyncFunction)
Futures.transform(ListenableFuture, AsyncFunction, Executor)

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文连接地址: [Google Guava] 4-函数式编程


7

沈义扬

本站的翻译主编。关注并发编程,面向对象设计,分布式系统。
FavoriteLoading添加本文到个人收藏
相关文章
相关标签/搜索