介绍

有时候你需要自己实现一些集合的扩展。在元素被添加到List的时候你可能需要添加一些特殊的行为,或者需要你一个支持数据库查询的Iterable。Guava为你同样也为我们自己提供了一系列的工具来简化这些操作。

Forwarding装饰器

Guava为所有集合接口提供了抽象的Forwarding类来简化装饰模式的使用。

Forwarding类仅定义了一个抽象方法 - delegate(),需要覆写它并返回一个被包装的对象。所有其他的方法都会直接使用返回的对象代理:例如ForwardingList.get(int)的就是简单的使用delegate.get(int)来实现的。

继承ForwardingXxx并实现delegate()方法,可以只实现目标类的某个或某些方法 — 不需要自己处理所有代理方法。

另外,很多方法都有一个standardMethod实现,可以用来恢复预期的行为,提供了相同的好处,例如扩展JDK的AbstractList或其他核心框架类。

来看一个例子。假设现在需要包装一个List,以实现当元素被添加进去时可以记录日志。当然,无论通过什么方式添加元素,都需要记录下来 — add(int, E)add(E)addAll(),所以必须覆写这些方法。

  1. class AddLoggingList<E> extends ForwardingList<E> {
  2. final List<E> delegate;
  3. @Override
  4. protected List<E> delegate() {
  5. return delegate;
  6. }
  7. @Override
  8. public void add(int index, E elem) {
  9. log(elem);
  10. super.add(int, E);
  11. }
  12. @Override
  13. public boolean add(E) {
  14. return standardAdd(elem);
  15. }
  16. @Override
  17. public boolean addAll(Collection<? extends E> c) {
  18. return standardAddAll(c);
  19. }
  20. }

注意,默认情况下所有方法都直接重定位到代理对象,所以覆写ForwardingMap.put()并不会改变ForwardingMap.putAll()的行为。覆写必须要改变默认行为的方法需要格外小心,并确保符合被包装的集合的限制。

一般情况,像AbstractList这样的框架类的大部分方法,在Forwarding包装器中已经提供了标准的实现。

提供特殊视图的接口有时同样提供了那些视图的standard实现。例如,ForwardingMap提供了standardKeySetstandardValuesstandardEntrySet,每个方法如果可能都被代理到了包装他们的Map中,如果不能被代理,则保持他们是抽象的(abstract method)。

接口 Forwarding装饰器
Collection ForwardingCollection
List ForwardingList
Set ForwardingSet
SortedSet ForwardingSortedSet
Map ForwardingMap
SortedMap ForwardingSortedMap
ConcurrentMap ForwardingConcurrentMap
Map.Entry ForwardingMapEntry
Queue ForwardingQueue
Iterator ForwardingIterator
ListIterator ForwardingListIterator
Multiset ForwardingMultiList
Multimap ForwardingMultimap
ListMultimap ForwardingListMultimap
SetMultimap ForwardingSetMultimap

PeekingIterator

有时候,标准的Iterator接口不能满足我们的需求。

Iterators支持Iterators.peekingIterator(Iterator)方法,它包装了一个Iterator并返回一个子类PeekongIterator,可以使用peek()返回下一次调用next()时返回的元素(按照字面意思,即是在还没有next()时偷窥下一个元素)。

注意,在调用peek()之后就不能再使用remove()方法了。

举例,去除连续重复的元素并添加到List:

  1. List<E> result = Lists.newArrayListT();
  2. PeekingIterator<E> iter = Iterators.peekingIterator(source.iterator());
  3. while (iter.haxNext()) {
  4. E current = iter.next();
  5. while (iter.hasNext() && iter.peek().equals(current)) {
  6. //跳过重复的元素
  7. iter.next();
  8. }
  9. result.add(current);
  10. }

AbstractIterator

想实现自己的Iterator吗?AbstractIterator会让这边的更加容易。

举例说明,假设我们需要包装一个Iterator用来忽略null值。

  1. public static Iterator<String> skipNulls(final Iterator<String> in) {
  2. return new AbstractIterator() {
  3. protected String computeNext() {
  4. while(in.hasNext()) {
  5. String s = in.next();
  6. if (s != null) {
  7. return s;
  8. }
  9. }
  10. return endOfData();
  11. }
  12. };
  13. }

我们只实现了一个computeNext()方法来计算下一个元素的值。当所有工作完成的时候,返回endOfData()来标识迭代的结束。

注意AbstractIterator继承自不允许remove()UnmodifiableIterator,如果需要一个支持remove()的迭代器,不要继承AbstractIterator

AbstractSequentialIterator

AbstractSequentialIterator提供了另一种表达迭代的方式。

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

这里我们实现了computeNext(T),它的参数是上一个迭代的值。

注意这里必须传入一个初始值,或者null(迭代立即结束)。computeNext()认为null值代表迭代的结束,也就是说,AbstractSequentialIterator不能用于实现可能包含null值的迭代器。