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添加本文到個人收藏
相關文章
相關標籤/搜索