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
不能用于实现可能返回空值的迭代器。