guava学习笔记(四)——函数式编程

本部分为guava笔记第四部分,主要整理guava中的函数式编程——Function和Predicate及其应用。

注意事项

截至JDK7,Java中也只能通过笨拙冗长的匿名类来达到近似函数式编程的效果。预计JDK8中会有所改变,但Guava现在就想给JDK5以上用户提供这类支持。

过度使用Guava函数式编程会导致冗长、混乱、可读性差而且低效的代码。这是迄今为止最容易(也是最经常)被滥用的部分,如果你想通过函数式风格达成一行代码,致使这行代码长到荒唐,Guava团队会泪流满面。

比较如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
};
Predicate<String> allCaps = new Predicate<String>() {
public boolean apply(String string) {
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
}
};
Multiset<Integer> lengths = HashMultiset.create(
Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction));

或FluentIterable的版本

1
2
3
4
5
6
7
8
9
10
11
12
Multiset<Integer> lengths = HashMultiset.create(
FluentIterable.from(strings)
.filter(new Predicate<String>() {
public boolean apply(String string) {
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
}
})
.transform(new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
}));

还有

1
2
3
4
5
6
Multiset<Integer> lengths = HashMultiset.create();
for (String string : strings) {
if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) {
lengths.add(string.length());
}
}

即使用了静态导入,甚至把Function和Predicate的声明放到别的文件,第一种代码实现仍然不简洁,可读性差并且效率较低。
截至JDK7,命令式代码仍应是默认和第一选择。不应该随便使用函数式风格,除非你绝对确定以下两点之一:

  • 使用函数式风格以后,整个工程的代码行会净减少。在上面的例子中,函数式版本用了11行, 命令式代码用了6行,把函数的定义放到另一个文件或常量中,并不能帮助减少总代码行。
  • 为了提高效率,转换集合的结果需要懒视图,而不是明确计算过的集合。此外,确保你已经阅读和重读了Effective Java的第55条,并且除了阅读本章后面的说明,你还真正做了性能测试并且有测试数据来证明函数式版本更快。

请务必确保,当使用Guava函数式的时候,用传统的命令式做同样的事情不会更具可读性。尝试把代码写下来,看看它是不是真的那么糟糕?会不会比你想尝试的极其笨拙的函数式 更具可读性。

Function和Predicate

本节只讨论直接与Function和Predicate打交道的Guava功能。一些其他工具类也和”函数式风格”相关,例如Iterables.concat(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

字符类型有自己特定版本的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) [compose(Function, Function)](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Functions.html#compose(com.google.common.base.Function, com.google.common.base.Function)) constant(T)
identity() toStringFunction()

细节请参考Javadoc。

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

instanceOf(Class) assignableFrom(Class) contains(Pattern)
in(Collection) isNull() alwaysFalse()
alwaysTrue() equalTo(Object) [compose(Predicate, Function)](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Predicates.html#compose(com.google.common.base.Predicate, com.google.common.base.Function))
and(Predicate...) or(Predicate...) not(Predicate)

细节请参考Javadoc。

使用函数式编程

Guava提供了很多工具方法,以便用Function或Predicate操作集合。这些方法通常可以在集合工具类找到,如Iterables,Lists,Sets,Maps,Multimaps等。

Predicate

Predicate的最基本应用就是过滤集合。所有Guava过滤方法都返回”视图”——译者注:即并非用一个新的集合表示过滤,而只是基于原集合的视图

集合类型 过滤方法
Iterable [Iterables.filter(Iterable, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#filter(java.lang.Iterable, com.google.common.base.Predicate))FluentIterable.filter(Predicate)
Iterator [Iterators.filter(Iterator, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#filter(java.util.Iterator, com.google.common.base.Predicate))
Collection [Collections2.filter(Collection, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Collections2.html#filter(java.util.Collection, com.google.common.base.Predicate))
Set [Sets.filter(Set, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#filter(java.util.Set, com.google.common.base.Predicate))
SortedSet [Sets.filter(SortedSet, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#filter(java.util.SortedSet, com.google.common.base.Predicate))
Map [Maps.filterKeys(Map, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#filterKeys(java.util.Map, com.google.common.base.Predicate))[Maps.filterValues(Map, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#filterValues(java.util.Map, com.google.common.base.Predicate))[Maps.filterEntries(Map, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#filterEntries(java.util.Map, com.google.common.base.Predicate))
SortedMap [Maps.filterKeys(SortedMap, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#filterKeys(java.util.SortedMap, com.google.common.base.Predicate))[Maps.filterValues(SortedMap, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#filterValues(java.util.SortedMap, com.google.common.base.Predicate))[Maps.filterEntries(SortedMap, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#filterEntries(java.util.SortedMap, com.google.common.base.Predicate))
Multimap [Multimaps.filterKeys(Multimap, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#filterKeys(com.google.common.collect.Multimap, com.google.common.base.Predicate))[Multimaps.filterValues(Multimap, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#filterValues(com.google.common.collect.Multimap, com.google.common.base.Predicate))[Multimaps.filterEntries(Multimap, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#filterEntries(com.google.common.collect.Multimap, com.google.common.base.Predicate))

List的过滤视图被省略了,因为不能有效地支持类似get(int)的操作。请改用Lists.newArrayList(Collections2.filter(list, predicate))做拷贝过滤。

除了简单过滤,Guava另外提供了若干用Predicate处理Iterable的工具——通常在Iterables工具类中,或者是FluentIterable的”fluent”(链式调用)方法。

Iterables方法签名 说明 另请参见
[boolean all(Iterable, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#all(java.lang.Iterable, com.google.common.base.Predicate)) 是否所有元素满足断言?懒实现:如果发现有元素不满足,不会继续迭代 [Iterators.all(Iterator, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#all(java.util.Iterator, com.google.common.base.Predicate))FluentIterable.allMatch(Predicate)
[boolean any(Iterable, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#any(java.lang.Iterable, com.google.common.base.Predicate)) 是否有任意元素满足元素满足断言?懒实现:只会迭代到发现满足的元素 [Iterators.any(Iterator, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#any(java.util.Iterator, com.google.common.base.Predicate))FluentIterable.anyMatch(Predicate)
[T find(Iterable, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#find(java.lang.Iterable, com.google.common.base.Predicate)) 循环并返回一个满足元素满足断言的元素,如果没有则抛出NoSuchElementException [Iterators.find(Iterator, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#find(java.util.Iterator, com.google.common.base.Predicate))[Iterables.find(Iterable, Predicate, T default)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#find(java.lang.Iterable, com.google.common.base.Predicate, T))[Iterators.find(Iterator, Predicate, T default)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#find(java.util.Iterator, com.google.common.base.Predicate, T))
[Optional tryFind(Iterable, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#tryFind(java.lang.Iterable, com.google.common.base.Predicate)) 返回一个满足元素满足断言的元素,若没有则返回Optional.absent() [Iterators.find(Iterator, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#find(java.util.Iterator, com.google.common.base.Predicate))[Iterables.find(Iterable, Predicate, T default)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#find(java.lang.Iterable, com.google.common.base.Predicate, T))[Iterators.find(Iterator, Predicate, T default)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#find(java.util.Iterator, com.google.common.base.Predicate, T))
[indexOf(Iterable, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#indexOf(java.lang.Iterable, com.google.common.base.Predicate)) 返回第一个满足元素满足断言的元素索引值,若没有返回-1 [Iterators.indexOf(Iterator, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#indexOf(java.util.Iterator, com.google.common.base.Predicate))
[removeIf(Iterable, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#removeIf(java.lang.Iterable, com.google.common.base.Predicate)) 移除所有满足元素满足断言的元素,实际调用Iterator.remove()方法 [Iterators.removeIf(Iterator, Predicate)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#removeIf(java.util.Iterator, com.google.common.base.Predicate))

Function

到目前为止,Function最常见的用途为转换集合。同样,所有的Guava转换方法也返回原集合的视图。

集合类型 转换方法
Iterable [Iterables.transform(Iterable, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#transform(java.lang.Iterable, com.google.common.base.Function))FluentIterable.transform(Function)
Iterator [Iterators.transform(Iterator, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#transform(java.util.Iterator, com.google.common.base.Function))
Collection [Collections2.transform(Collection, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Collections2.html#transform(java.util.Collection, com.google.common.base.Function))
List [Lists.transform(List, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#transform(java.util.List, com.google.common.base.Function))
Map [Maps.transformValues(Map, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#transformValues(java.util.Map, com.google.common.base.Function))[Maps.transformEntries(Map, EntryTransformer)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#transformEntries(java.util.Map, com.google.common.collect.Maps.EntryTransformer))
SortedMap [Maps.transformValues(SortedMap, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#transformValues(java.util.SortedMap, com.google.common.base.Function))[Maps.transformEntries(SortedMap, EntryTransformer)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#transformEntries(java.util.SortedMap, com.google.common.collect.Maps.EntryTransformer))
Multimap [Multimaps.transformValues(Multimap, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#transformValues(com.google.common.collect.Multimap, com.google.common.base.Function))[Multimaps.transformEntries(Multimap, EntryTransformer)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#transformEntries(com.google.common.collect.Multimap, com.google.common.collect.Maps.EntryTransformer))
ListMultimap [Multimaps.transformValues(ListMultimap, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#transformValues(com.google.common.collect.ListMultimap, com.google.common.base.Function))[Multimaps.transformEntries(ListMultimap, EntryTransformer)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#transformEntries(com.google.common.collect.ListMultimap, com.google.common.collect.Maps.EntryTransformer))
Table [Tables.transformValues(Table, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Tables.html#transformValues(com.google.common.collect.Table, com.google.common.base.Function))

Map和Multimap有特殊的方法,其中有个EntryTransformer参数,它可以使用旧的键值来计算,并且用计算结果替换旧值。

对Set的转换操作被省略了,因为不能有效支持contains(Object)操作——译者注:懒视图实际上不会全部计算转换后的Set元素,因此不能高效地支持contains(Object)。请改用Sets.newHashSet(Collections2.transform(set, function))进行拷贝转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
List<String> names;
Map<String, Person> personWithName;
List<Person> people = Lists.transform(names, Functions.forMap(personWithName));

ListMultimap<String, String> firstNameToLastNames;
// maps first names to all last names of people with that first name

ListMultimap<String, String> firstNameToName = Multimaps.transformEntries(firstNameToLastNames,
new EntryTransformer<String, String, String> () {
public String transformEntry(String firstName, String lastName) {
return firstName + " " + lastName;
}
});

可以组合Function使用的类包括:

Ordering Ordering.onResultOf(Function)
Predicate [Predicates.compose(Predicate, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Predicates.html#compose(com.google.common.base.Predicate, com.google.common.base.Function))
Equivalence Equivalence.onResultOf(Function)
Supplier [Suppliers.compose(Function, Supplier)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Suppliers.html#compose(com.google.common.base.Function, com.google.common.base.Supplier))
Function [Functions.compose(Function, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Functions.html#compose(com.google.common.base.Function, com.google.common.base.Function))

此外,ListenableFuture API支持转换ListenableFuture。Futures也提供了接受AsyncFunction参数的方法。AsyncFunction是Function的变种,它允许异步计算值。

[Futures.transform(ListenableFuture, Function)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Futures.html#transform(com.google.common.util.concurrent.ListenableFuture, com.google.common.base.Function))
[Futures.transform(ListenableFuture, Function, Executor)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Futures.html#transform(com.google.common.util.concurrent.ListenableFuture, com.google.common.base.Function, java.util.concurrent.Executor))
[Futures.transform(ListenableFuture, AsyncFunction)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Futures.html#transform(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.AsyncFunction))
[Futures.transform(ListenableFuture,AsyncFunction, Executor)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Futures.html#transform(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.AsyncFunction, java.util.concurrent.Executor))

非原创内容,转载自并发编程网 – ifeve.com

坚持原创技术分享,您的支持将鼓励我继续创作!
分享到: