原文連接 譯文連接 譯者:沈義揚,校對:丁一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)
添加本文到個人收藏