天天看点

[Google Guava] 4-函数式编程

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

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

比较如下代码:

<code>01</code>

<code>function&lt;string, integer&gt; lengthfunction =</code><code>new</code> <code>function&lt;string, integer&gt;() {</code>

<code>02</code>

<code>    </code><code>public</code> <code>integer apply(string string) {</code>

<code>03</code>

<code>        </code><code>return</code> <code>string.length();</code>

<code>04</code>

<code>    </code><code>}</code>

<code>05</code>

<code>};</code>

<code>06</code>

<code>predicate&lt;string&gt; allcaps =</code><code>new</code> <code>predicate&lt;string&gt;() {</code>

<code>07</code>

<code>    </code><code>public</code> <code>boolean</code> <code>apply(string string) {</code>

<code>08</code>

<code>        </code><code>return</code> <code>charmatcher.java_upper_case.matchesallof(string);</code>

<code>09</code>

<code>10</code>

<code>11</code>

<code>multiset&lt;integer&gt; lengths = hashmultiset.create(</code>

<code>12</code>

<code>     </code><code>iterables.transform(iterables.filter(strings, allcaps), lengthfunction));</code>

或fluentiterable的版本

<code>    </code><code>fluentiterable.from(strings)</code>

<code>        </code><code>.filter(</code><code>new</code> <code>predicate&lt;string&gt;() {</code>

<code>            </code><code>public</code> <code>boolean</code> <code>apply(string string) {</code>

<code>                </code><code>return</code> <code>charmatcher.java_upper_case.matchesallof(string);</code>

<code>            </code><code>}</code>

<code>        </code><code>})</code>

<code>        </code><code>.transform(</code><code>new</code> <code>function&lt;string, integer&gt;() {</code>

<code>            </code><code>public</code> <code>integer apply(string string) {</code>

<code>                </code><code>return</code> <code>string.length();</code>

<code>        </code><code>}));</code>

还有

<code>1</code>

<code>multiset&lt;integer&gt; lengths = hashmultiset.create();</code>

<code>2</code>

<code>for</code> <code>(string string : strings) {</code>

<code>3</code>

<code>    </code><code>if</code> <code>(charmatcher.java_upper_case.matchesallof(string)) {</code>

<code>4</code>

<code>        </code><code>lengths.add(string.length());</code>

<code>5</code>

<code>6</code>

<code>}</code>

即使用了静态导入,甚至把function和predicate的声明放到别的文件,第一种代码实现仍然不简洁,可读性差并且效率较低。

截至jdk7,命令式代码仍应是默认和第一选择。不应该随便使用函数式风格,除非你绝对确定以下两点之一:

使用函数式风格以后,整个工程的代码行会净减少。在上面的例子中,函数式版本用了11行, 命令式代码用了6行,把函数的定义放到另一个文件或常量中,并不能帮助减少总代码行。

为了提高效率,转换集合的结果需要懒视图,而不是明确计算过的集合。此外,确保你已经阅读和重读了effective java的第55条,并且除了阅读本章后面的说明,你还真正做了性能测试并且有测试数据来证明函数式版本更快。

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

guava提供两个基本的函数式接口:

function&lt;a, b&gt;,它声明了单个方法b apply(a input)。function对象通常被预期为引用透明的——没有副作用——并且引用透明性中的”相等”语义与equals一致,如a.equals(b)意味着function.apply(a).equals(function.apply(b))。

predicate&lt;t&gt;,它声明了单个方法boolean apply(t input)。predicate对象通常也被预期为无副作用函数,并且”相等”语义与equals一致。

此外,对可比较类型和基于比较逻辑的predicate,range类可以满足大多数需求——它表示一个不可变区间。range类实现了predicate,用以判断值是否在区间内。例如,range.atmost(2)就是个完全合法的predicate&lt;integer&gt;。更多使用range的细节请参照第8章。

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#formap(java.util.map)">formap(map&lt;a, b&gt;)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#compose(com.google.common.base.function,%20com.google.common.base.function)">compose(function&lt;b, c&gt;, function&lt;a, b&gt;)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#constant(e)">constant(t)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#identity()">identity()</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#tostringfunction()">tostringfunction()</a>

细节请参考javadoc。

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#instanceof(java.lang.class)">instanceof(class)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#assignablefrom(java.lang.class)">assignablefrom(class)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#contains(java.util.regex.pattern)">contains(pattern)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#in(java.util.collection)">in(collection)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#isnull()">isnull()</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#alwaysfalse()">alwaysfalse()</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#alwaystrue()">alwaystrue()</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#equalto(t)">equalto(object)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#compose(com.google.common.base.predicate,%20com.google.common.base.function)">compose(predicate, function)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#and(com.google.common.base.predicate...)">and(predicate...)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#or(com.google.common.base.predicate...)">or(predicate...)</a>

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#not(com.google.common.base.predicate)">not(predicate)</a>

guava提供了很多工具方法,以便用function或predicate操作集合。这些方法通常可以在集合工具类找到,如iterables,lists,sets,maps,multimaps等。

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

<b>集合类型</b><b></b>

<b>过滤方法</b><b></b>

iterable

iterator

collection

set

sortedset

map

sortedmap

multimap

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

<b>iterables</b><b>方法签名</b><b></b>

<b>说明</b><b></b>

<b>另请参见</b><b></b>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#all(java.lang.iterable,%20com.google.common.base.predicate)">boolean all(iterable, predicate)</a>

是否所有元素满足断言?懒实现:如果发现有元素不满足,不会继续迭代

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#any(java.lang.iterable,%20com.google.common.base.predicate)">boolean any(iterable, predicate)</a>

是否有任意元素满足元素满足断言?懒实现:只会迭代到发现满足的元素

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#find(java.lang.iterable,%20com.google.common.base.predicate)">t find(iterable, predicate)</a>

循环并返回<b>一个</b>满足元素满足断言的元素,如果没有则抛出nosuchelementexception

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#find(java.util.iterator,%20com.google.common.base.predicate)">iterators.find(iterator, predicate)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#find(java.lang.iterable,%20com.google.common.base.predicate,%20t)">iterables.find(iterable, predicate, t default)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#find(java.util.iterator,%20com.google.common.base.predicate,%20t)">iterators.find(iterator, predicate, t default)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#tryfind(java.lang.iterable,%20com.google.common.base.predicate)">optional&lt;t&gt; tryfind(iterable, predicate)</a>

返回<b>一个</b>满足元素满足断言的元素,若没有则返回optional.absent()

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#indexof(java.lang.iterable,%20com.google.common.base.predicate)">indexof(iterable, predicate)</a>

返回第一个满足元素满足断言的元素索引值,若没有返回-1

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#indexof(java.util.iterator,%20com.google.common.base.predicate)">iterators.indexof(iterator, predicate)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#removeif(java.lang.iterable,%20com.google.common.base.predicate)">removeif(iterable, predicate)</a>

移除所有满足元素满足断言的元素,实际调用iterator.remove()方法

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#removeif(java.util.iterator,%20com.google.common.base.predicate)">iterators.removeif(iterator, predicate)</a>

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

<b>转换</b><b>方法</b><b></b>

list

map*

sortedmap*

multimap*

listmultimap*

table

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

<code>list&lt;string&gt; names;</code>

<code>map&lt;string, person&gt; personwithname;</code>

<code>list&lt;person&gt; people = lists.transform(names, functions.formap(personwithname));</code>

<code>listmultimap&lt;string, string&gt; firstnametolastnames;</code>

<code>// maps first names to all last names of people with that first name</code>

<code>listmultimap&lt;string, string&gt; firstnametoname = multimaps.transformentries(firstnametolastnames,</code>

<code>    </code><code>new</code> <code>entrytransformer&lt;string, string, string&gt; () {</code>

<code>        </code><code>public</code> <code>string transformentry(string firstname, string lastname) {</code>

<code>            </code><code>return</code> <code>firstname +</code><code>" "</code> <code>+ lastname;</code>

<code>        </code><code>}</code>

<code>13</code>

<code>    </code><code>});</code>

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

ordering

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ordering.html#onresultof(com.google.common.base.function)">ordering.onresultof(function)</a>

predicate

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/predicates.html#compose(com.google.common.base.predicate,%20com.google.common.base.function)">predicates.compose(predicate, function)</a>

equivalence

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/equivalence.html#onresultof(com.google.common.base.function)">equivalence.onresultof(function)</a>

supplier

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/suppliers.html#compose(com.google.common.base.function,%20com.google.common.base.supplier)">suppliers.compose(function, supplier)</a>

function

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/functions.html#compose(com.google.common.base.function,%20com.google.common.base.function)">functions.compose(function, function)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.base.function)">futures.transform(listenablefuture, function)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.base.function,%20java.util.concurrent.executor)">futures.transform(listenablefuture, function, executor)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.util.concurrent.asyncfunction)">futures.transform(listenablefuture, asyncfunction)</a>

<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.util.concurrent.asyncfunction,%20java.util.concurrent.executor)">futures.transform(listenablefuture, asyncfunction, executor)</a>