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

集合擴(kuò)展工具類

簡介

有時候你需要實(shí)現(xiàn)自己的集合擴(kuò)展。也許你想要在元素被添加到列表時增加特定的行為,或者你想實(shí)現(xiàn)一個 Iterable,其底層實(shí)際上是遍歷數(shù)據(jù)庫查詢的結(jié)果集。Guava 為你,也為我們自己提供了若干工具方法,以便讓類似的工作變得更簡單。(畢竟,我們自己也要用這些工具擴(kuò)展集合框架。)

Forwarding裝飾器

針對所有類型的集合接口,Guava 都提供了 Forwarding 抽象類以簡化裝飾者模式的使用。

Forwarding 抽象類定義了一個抽象方法:delegate(),你可以覆蓋這個方法來返回被裝飾對象。所有其他方法都會直接委托給 delegate()。例如說:ForwardingList.get(int)實(shí)際上執(zhí)行了 delegate().get(int)。

通過創(chuàng)建 ForwardingXXX 的子類并實(shí)現(xiàn) delegate()方法,可以選擇性地覆蓋子類的方法來增加裝飾功能,而不需要自己委托每個方法——譯者注:因?yàn)樗蟹椒ǘ寄J(rèn)委托給 delegate()返回的對象,你可以只覆蓋需要裝飾的方法。

此外,很多集合方法都對應(yīng)一個”標(biāo)準(zhǔn)方法[standardxxx]”實(shí)現(xiàn),可以用來恢復(fù)被裝飾對象的默認(rèn)行為,以提供相同的優(yōu)點(diǎn)。比如在擴(kuò)展 AbstractList 或 JDK 中的其他骨架類時,可以使用類似 standardAddAll 這樣的方法。

讓我們看看這個例子。假定你想裝飾一個 List,讓其記錄所有添加進(jìn)來的元素。當(dāng)然,無論元素是用什么方法——add(int, E), add(E), 或 addAll(Collection)——添加進(jìn)來的,我們都希望進(jìn)行記錄,因此我們需要覆蓋所有這些方法。


    class AddLoggingList<E> extends ForwardingList<E> {
        final List<E> delegate; // backing list
        @Override protected List<E> delegate() {
            return delegate;
        }
        @Override public void add(int index, E elem) {
            log(index, elem);
            super.add(index, elem);
        }
        @Override public boolean add(E elem) {
            return standardAdd(elem); // 用add(int, E)實(shí)現(xiàn)
        }
        @Override public boolean addAll(Collection<? extends E> c) {
            return standardAddAll(c); // 用add實(shí)現(xiàn)
        }
    }

記住,默認(rèn)情況下,所有方法都直接轉(zhuǎn)發(fā)到被代理對象,因此覆蓋 ForwardingMap.put 并不會改變 ForwardingMap.putAll 的行為。小心覆蓋所有需要改變行為的方法,并且確保裝飾后的集合滿足接口契約。

通常來說,類似于 AbstractList 的抽象集合骨架類,其大多數(shù)方法在 Forwarding 裝飾器中都有對應(yīng)的”標(biāo)準(zhǔn)方法”實(shí)現(xiàn)。

對提供特定視圖的接口,F(xiàn)orwarding 裝飾器也為這些視圖提供了相應(yīng)的”標(biāo)準(zhǔn)方法”實(shí)現(xiàn)。例如,F(xiàn)orwardingMap 提供 StandardKeySet、StandardValues 和 StandardEntrySet 類,它們在可以的情況下都會把自己的方法委托給被裝飾的 Map,把不能委托的聲明為抽象方法。

PeekingIterator

有時候,普通的 Iterator 接口還不夠。

Iterators 提供一個 Iterators.peekingIterator(Iterator)方法,來把 Iterator 包裝為 PeekingIterator,這是 Iterator 的子類,它能讓你事先窺視[[peek()](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/PeekingIterator.html#peek())]到下一次調(diào)用 next()返回的元素。

注意:Iterators.peekingIterator 返回的 PeekingIterator 不支持在 peek()操作之后調(diào)用remove()方法。

舉個例子:復(fù)制一個 List,并去除連續(xù)的重復(fù)元素。


    List<E> result = Lists.newArrayList();
    PeekingIterator<E> iter = Iterators.peekingIterator(source.iterator());
    while (iter.hasNext()) {
        E current = iter.next();
        while (iter.hasNext() && iter.peek().equals(current)) {
            //跳過重復(fù)的元素
            iter.next();
        }
        result.add(current);
    }

傳統(tǒng)的實(shí)現(xiàn)方式需要記錄上一個元素,并在特定情況下后退,但這很難處理且容易出錯。相較而言, PeekingIterator 在理解和使用上就比較直接了。

AbstractIterator

實(shí)現(xiàn)你自己的 Iterator?AbstractIterator 讓生活更輕松。

用一個例子來解釋 AbstractIterator 最簡單。比方說,我們要包裝一個 iterator 以跳過空值。


    public static Iterator<String> skipNulls(final Iterator<String> in) {
        return new AbstractIterator<String>() {
            protected String computeNext() {
                while (in.hasNext()) {
                    String s = in.next();
                    if (s != null) {
                        return s;
                    }
                }
                return endOfData();
            }
        };
    }

你實(shí)現(xiàn)了 [computeNext()](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/AbstractIterator.html#computeNext())方法,來計算下一個值。如果循環(huán)結(jié)束了也沒有找到下一個值,請返回 endOfData()表明已經(jīng)到達(dá)迭代的末尾。

注意:AbstractIterator 繼承了 UnmodifiableIterator,所以禁止實(shí)現(xiàn) remove()方法。如果你需要支持 remove()的迭代器,就不應(yīng)該繼承 AbstractIterator。

AbstractSequentialIterator

有一些迭代器用其他方式表示會更簡單。AbstractSequentialIterator 就提供了表示迭代的另一種方式。


    Iterator<Integer> powersOfTwo = new AbstractSequentialIterator<Integer>(1) { // 注意初始值1!
        protected Integer computeNext(Integer previous) {
        return (previous == 1 << 30) ? null : previous * 2;
    }
    };

我們在這兒實(shí)現(xiàn)了 computeNext(T)方法,它能接受前一個值作為參數(shù)。

注意,你必須額外傳入一個初始值,或者傳入 null 讓迭代立即結(jié)束。因?yàn)?computeNext(T)假定 null 值意味著迭代的末尾——AbstractSequentialIterator 不能用來實(shí)現(xiàn)可能返回 null 的迭代器。

上一篇:使用和避免 null下一篇:區(qū)間