鍍金池/ 教程/ Java/ 函數(shù)式編程
不可變集合
排序: Guava 強大的”流暢風(fēng)格比較器”
強大的集合工具類:java.util.Collections 中未包含的集合工具
新集合類型
常見 Object 方法
I/O
前置條件
字符串處理:分割,連接,填充
散列
原生類型
數(shù)學(xué)運算
使用和避免 null
Throwables:簡化異常和錯誤的傳播與檢查
google Guava 包的 ListenableFuture 解析
事件總線
緩存
函數(shù)式編程
區(qū)間
集合擴展工具類
Google-Guava Concurrent 包里的 Service 框架淺析
google Guava 包的 reflection 解析

函數(shù)式編程

注意事項

截至 JDK7,Java 中也只能通過笨拙冗長的匿名類來達到近似函數(shù)式編程的效果。預(yù)計 JDK8 中會有所改變,但 Guava 現(xiàn)在就想給 JDK5 以上用戶提供這類支持。

過度使用 Guava 函數(shù)式編程會導(dǎo)致冗長、混亂、可讀性差而且低效的代碼。這是迄今為止最容易(也是最經(jīng)常)被濫用的部分,如果你想通過函數(shù)式風(fēng)格達成一行代碼,致使這行代碼長到荒唐,Guava 團隊會淚流滿面。

比較如下代碼:


    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 的版本


    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();
                }
            }));

還有


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

即使用了靜態(tài)導(dǎo)入,甚至把 Function 和 Predicate 的聲明放到別的文件,第一種代碼實現(xiàn)仍然不簡潔,可讀性差并且效率較低。

截至 JDK7,命令式代碼仍應(yīng)是默認和第一選擇。不應(yīng)該隨便使用函數(shù)式風(fēng)格,除非你絕對確定以下兩點之一:

  • 使用函數(shù)式風(fēng)格以后,整個工程的代碼行會凈減少。在上面的例子中,函數(shù)式版本用了 11 行, 命令式代碼用了 6 行,把函數(shù)的定義放到另一個文件或常量中,并不能幫助減少總代碼行。
  • 為了提高效率,轉(zhuǎn)換集合的結(jié)果需要懶視圖,而不是明確計算過的集合。此外,確保你已經(jīng)閱讀和重讀了 Effective Java 的第 55 條,并且除了閱讀本章后面的說明,你還真正做了性能測試并且有測試數(shù)據(jù)來證明函數(shù)式版本更快。

請務(wù)必確保,當使用 Guava 函數(shù)式的時候,用傳統(tǒng)的命令式做同樣的事情不會更具可讀性。嘗試把代碼寫下來,看看它是不是真的那么糟糕?會不會比你想嘗試的極其笨拙的函數(shù)式 更具可讀性。

Functions[函數(shù)]和 Predicates[斷言]

本節(jié)只討論直接與 Function 和 Predicate 打交道的 Guava 功能。一些其他工具類也和”函數(shù)式風(fēng)格”相關(guān),例如 Iterables.concat(Iterable),和其他用常量時間返回視圖的方法。嘗試看看 2.3 節(jié)的集合工具類。

Guava 提供兩個基本的函數(shù)式接口:

  • Function<A, B>,它聲明了單個方法 B apply(A input)。Function 對象通常被預(yù)期為引用透明的——沒有副作用——并且引用透明性中的”相等”語義與 equals 一致,如 a.equals(b)意味著 function.apply(a).equals(function.apply(b))。
  • Predicate,它聲明了單個方法 boolean apply(T input)。Predicate 對象通常也被預(yù)期為無副作用函數(shù),并且”相等”語義與 equals 一致。

特殊的斷言

字符類型有自己特定版本的 Predicate——CharMatcher,它通常更高效,并且在某些需求方面更有用。CharMatcher 實現(xiàn)了 Predicate,可以當作 Predicate 一樣使用,要把 Predicate 轉(zhuǎn)成 CharMatcher,可以使用 CharMatcher.forPredicate。更多細節(jié)請參考第 6 章-字符串處理。

此外,對可比較類型和基于比較邏輯的 Predicate,Range 類可以滿足大多數(shù)需求——它表示一個不可變區(qū)間。Range 類實現(xiàn)了 Predicate,用以判斷值是否在區(qū)間內(nèi)。例如,Range.atMost(2)就是個完全合法的 Predicate。更多使用 Range 的細節(jié)請參照第8章。

操作 Functions 和 Predicates

Functions 提供簡便的 Function 構(gòu)造和操作方法,包括:

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

細節(jié)請參考 Javadoc。

相應(yīng)地,Predicates 提供了更多構(gòu)造和處理 Predicate 的方法,下面是一些例子:

instanceOf(Class) assignableFrom(Class) contains(Pattern)
in(Collection) isNull() alwaysFalse()
alwaysTrue() equalTo(Object) compose(Predicate, Function)
and(Predicate...) or(Predicate...) not(Predicate)

細節(jié)請參考Javadoc。

使用函數(shù)式編程

Guava 提供了很多工具方法,以便用 Function 或 Predicate 操作集合。這些方法通??梢栽诩瞎ぞ哳愓业?,如 Iterables,Lists,Sets,Maps,Multimaps 等。

斷言

斷言的最基本應(yīng)用就是過濾集合。所有 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”(鏈式調(diào)用)方法。

Iterables方法簽名 說明 另請參見
boolean all(Iterable, Predicate) 是否所有元素滿足斷言?懶實現(xiàn):如果發(fā)現(xiàn)有元素不滿足,不會繼續(xù)迭代 Iterators.all(Iterator, Predicate)FluentIterable.allMatch(Predicate)
boolean any(Iterable, Predicate) 是否有任意元素滿足元素滿足斷言?懶實現(xiàn):只會迭代到發(fā)現(xiàn)滿足的元素 Iterators.any(Iterator, Predicate)FluentIterable.anyMatch(Predicate)
T find(Iterable, Predicate) 循環(huán)并返回一個滿足元素滿足斷言的元素,如果沒有則拋出 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) 移除所有滿足元素滿足斷言的元素,實際調(diào)用Iterator.remove()方法 Iterators.removeIf(Iterator, Predicate)

函數(shù)

到目前為止,函數(shù)最常見的用途為轉(zhuǎn)換集合。同樣,所有的 Guava 轉(zhuǎn)換方法也返回原集合的視圖。

集合類型 轉(zhuǎn)換方法
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>參數(shù),它可以使用舊的鍵值來計算,并且用計算結(jié)果替換舊值。

對 Set 的轉(zhuǎn)換操作被省略了,因為不能有效支持 contains(Object)操作——譯者注:懶視圖實際上不會全部計算轉(zhuǎn)換后的 Set 元素,因此不能高效地支持 contains(Object)。*請改用 Sets.newHashSet(Collections2.transform(set, function))進行拷貝轉(zhuǎn)換。


    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)
Equivalence Equivalence.onResultOf(Function)
Supplier Suppliers.compose(Function, Supplier)
Function Functions.compose(Function, Function)

此外,ListenableFuture API 支持轉(zhuǎn)換 ListenableFuture。Futures 也提供了接受 AsyncFunction 參數(shù)的方法。

AsyncFunction 是 Function 的變種,它允許異步計算值。

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