# 2.4-集合擴展工具類
[原文鏈接](http://code.google.com/p/guava-libraries/wiki/CollectionHelpersExplained) [譯文鏈接](http://ifeve.com/google-guava-collectionhelpersexplained)?譯者:沈義揚,校對:丁一
## 簡介
有時候你需要實現自己的集合擴展。也許你想要在元素被添加到列表時增加特定的行為,或者你想實現一個Iterable,其底層實際上是遍歷數據庫查詢的結果集。Guava為你,也為我們自己提供了若干工具方法,以便讓類似的工作變得更簡單。(畢竟,我們自己也要用這些工具擴展集合框架。)
## Forwarding裝飾器
針對所有類型的集合接口,Guava都提供了Forwarding抽象類以簡化[裝飾者模式](http://en.wikipedia.org/wiki/Decorator_pattern)的使用。
Forwarding抽象類定義了一個抽象方法:delegate(),你可以覆蓋這個方法來返回被裝飾對象。所有其他方法都會直接委托給delegate()。例如說:ForwardingList.get(int)實際上執行了delegate().get(int)。
通過創建ForwardingXXX的子類并實現delegate()方法,可以選擇性地覆蓋子類的方法來增加裝飾功能,而不需要自己委托每個方法——_譯者注:因為所有方法都默認委托給__delegate()返回的對象,你可以只覆蓋需要裝飾的方法。_
此外,很多集合方法都對應一個”標準方法[standardxxx]”實現,可以用來恢復被裝飾對象的默認行為,以提供相同的優點。比如在擴展AbstractList或JDK中的其他骨架類時,可以使用類似standardAddAll這樣的方法。
讓我們看看這個例子。假定你想裝飾一個List,讓其記錄所有添加進來的元素。當然,無論元素是用什么方法——add(int, E), add(E), 或addAll(Collection)——添加進來的,我們都希望進行記錄,因此我們需要覆蓋所有這些方法。
```
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)實現
}
@Override public boolean addAll(Collection<? extends E> c) {
return standardAddAll(c); // 用add實現
}
}
```
記住,默認情況下,所有方法都直接轉發到被代理對象,因此覆蓋ForwardingMap.put并不會改變ForwardingMap.putAll的行為。小心覆蓋所有需要改變行為的方法,并且確保裝飾后的集合滿足接口契約。
通常來說,類似于AbstractList的抽象集合骨架類,其大多數方法在Forwarding裝飾器中都有對應的”標準方法”實現。
對提供特定視圖的接口,Forwarding裝飾器也為這些視圖提供了相應的”標準方法”實現。例如,ForwardingMap提供StandardKeySet、StandardValues和StandardEntrySet類,它們在可以的情況下都會把自己的方法委托給被裝飾的Map,把不能委托的聲明為抽象方法。
## PeekingIterator
有時候,普通的Iterator接口還不夠。
Iterators提供一個[`Iterators.peekingIterator(Iterator)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html#peekingIterator%28java.util.Iterator%29)方法,來把Iterator包裝為[`PeekingIterator`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/PeekingIterator.html),這是Iterator的子類,它能讓你事先窺視[[`peek()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/PeekingIterator.html#peek%28%29)]到下一次調用next()返回的元素。
注意:Iterators.peekingIterator返回的PeekingIterator不支持在peek()操作之后調用remove()方法。
舉個例子:復制一個List,并去除連續的重復元素。
```
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)) {
//跳過重復的元素
iter.next();
}
result.add(current);
}
```
傳統的實現方式需要記錄上一個元素,并在特定情況下后退,但這很難處理且容易出錯。相較而言,PeekingIterator在理解和使用上就比較直接了。
## AbstractIterator
實現你自己的Iterator?[`AbstractIterator`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/AbstractIterator.html)讓生活更輕松。
用一個例子來解釋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();
}
};
}
```
你實現了[`computeNext()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/AbstractIterator.html#computeNext%28%29)方法,來計算下一個值。如果循環結束了也沒有找到下一個值,請返回endOfData()表明已經到達迭代的末尾。
注意:AbstractIterator繼承了UnmodifiableIterator,所以禁止實現remove()方法。如果你需要支持remove()的迭代器,就不應該繼承AbstractIterator。
### AbstractSequentialIterator
有一些迭代器用其他方式表示會更簡單。[`AbstractSequentialIterator`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/AbstractSequentialIterator.html) 就提供了表示迭代的另一種方式。
```
Iterator<Integer> powersOfTwo = new AbstractSequentialIterator<Integer>(1) { // 注意初始值1!
protected Integer computeNext(Integer previous) {
return (previous == 1 << 30) ? null : previous * 2;
}
};
```
我們在這兒實現了[`computeNext(T)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/AbstractSequentialIterator.html#computeNext%28T%29)方法,它能接受前一個值作為參數。
注意,你必須額外傳入一個初始值,或者傳入null讓迭代立即結束。因為computeNext(T)假定null值意味著迭代的末尾——AbstractSequentialIterator不能用來實現可能返回null的迭代器。
- Google Guava官方教程(中文版)
- 1-基本工具
- 1.1-使用和避免null
- 1.2-前置條件
- 1.3-常見Object方法
- 1.4-排序: Guava強大的”流暢風格比較器”
- 1.5-Throwables:簡化異常和錯誤的傳播與檢查
- 2-集合
- 2.1-不可變集合
- 2.2-新集合類型
- 2.3-強大的集合工具類:java.util.Collections中未包含的集合工具
- 2.4-集合擴展工具類
- 3-緩存
- 4-函數式編程
- 5-并發
- 5.1-google Guava包的ListenableFuture解析
- 5.2-Google-Guava Concurrent包里的Service框架淺析
- 6-字符串處理:分割,連接,填充
- 7-原生類型
- 9-I/O
- 10-散列
- 11-事件總線
- 12-數學運算
- 13-反射