Guava学习之Collections ——Collection Helpers

Collection Helpers

Introduction

有时候需要写自己的扩展集合类。你可能需要当给list中添加元素的时候加入特殊的行为,或者想写一个由数据库查询支持的Iterable 。Guava 提供的工具类使得你在做这些任务的时候更容易。 (毕竟,我们自己的业务扩展了集合框架。)

Forwarding Decorators

对于所有的集合接口,For all the various collection interfaces, Guava 提供 Forwarding 抽象类以简化使用 decorator pattern(装饰器模式)。

Forwarding 类 定义了一个抽象方法, delegate(), 你应该重写去返回一个装饰类。其他的方法都直接被委托:例如,ForwardingList.get(int)是delegate().get(int)的简单实现。

通过子类化 ForwardingXXX 并实现 delegate() 方法, 可以只重写目标类中的选定方法,添加修饰的功能而不必自己委托每个方法。

此外, 许多方法实现了 standardMethod ,可以恢复预期的行为,提供一些AbstractList 的扩展或者JDK中的框架类中的相似的行为。

让我们举个例子。假设你想装饰一个列表,以便它记录添加到它里面的所有元素。当然,无论使用哪个方法添加元素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); // implements in terms of add(int, E) 
    } 
    @Override 
    public boolean addAll(Collection<? extends E> c) { 
        return standardAddAll(c); // implements in terms of add 
    } 
}

记住,默认情况下,所有方法都直接转发给委托,因此重写 ForwardingMap.put 将不会改变 ForwardingMap.putAll 的行为。 请注意覆盖必须更改其行为的每个方法,并确保装饰后的集合满足其契约。

通常, 抽象几何框架如AbstractList 提供的大多数方法在Forwarding 装饰器中也作为标准实现提供。

提供特殊视图的接口有时提供这些视图的标准实现。例如, ForwardingMap  提供 tandardKeySet, StandardValues, 与 StandardEntrySet, 其中每个方法都尽可能地将其方法委托给修饰过的映射,否则,它们将留下不能被委托为抽象的方法。其中每个方法都尽可能地将其方法委托给修饰过的映射,否则,它们将留下不能被委托为抽象的方法。

Interface Forwarding Decorator
Collection ForwardingCollection
List ForwardingList
Set ForwardingSet
SortedSet ForwardingSortedSet
Map ForwardingMap
SortedMap ForwardingSortedMap
ConcurrentMap ForwardingConcurrentMap
Map.Entry ForwardingMapEntry
Queue ForwardingQueue
Iterator ForwardingIterator
ListIterator ForwardingListIterator
Multiset ForwardingMultiset
Multimap ForwardingMultimap
ListMultimap ForwardingListMultimap
SetMultimap ForwardingSetMultimap

PeekingIterator

有时候普通的Iterator 接口不够。

Iterators 支持方法 Iterators.peekingIterator(Iterator), 其 包装了一个 Iterator 返回 PeekingIterator, 是一种Iterator 的子类能够让你 peek() 在下一次调用next()时返回的元素。

注意: Iterators 返回 PeekingIterator 。peekingIterator 不支持 remove() 在调用 peek()之后.

让我们举个例子:复制一个列表,同时删除连续的重复元素。

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)) { 
       // skip this duplicate element 
       iter.next(); 
    } 
    result.add(current); 
}

传统的实现方法包括跟踪前一个元素,并在特定条件下回退,但这是一项棘手且易出错的业务。 PeekingIterator 比较容易理解和使用。

AbstractIterator

实现你自己的 Iterator? AbstractIterator 使你的生活更轻松。

用一个例子来解释是最容易的。假设我们想要包装一个迭代器以便跳过空值。

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(), 仅仅计算下一个值,完成序列后,只返回 endOfData() 以标记迭代结束。

注意: AbstractIterator   继承unmodifiableIterator, 其禁止实现 remove()方法。如果你想要 iterator 支持 remove(), 那就不能继承 AbstractIterator.

AbstractSequentialIterator

一些迭代器更容易用其他方式表达。AbstractSequentialIterator 提供另一种表示迭代的方式。

Iterator<Integer> powersOfTwo = new AbstractSequentialIterator<Integer>(1) { 
    // note the initial value! 
    protected Integer computeNext(Integer previous) { 
        return (previous == 1 << 30) ? null : previous * 2; 
    } 
};

这里我们实现的方法 computeNext(T)接受先前的值作为参数。

注意,如果迭代器应该立即结束,则必须另外传递初始值,或者null。computeNext 假设空值意味着迭代结束-- AbstractSequentialIterator 不能用于实现可能返回空值的迭代器。

猜你喜欢

转载自blog.csdn.net/yshuoo/article/details/82740506
今日推荐