GUAVA--集合(集合擴展工具類)

1、簡介

有時候你須要實現本身的集合擴展。也許你想要在元素被添加到列表時增長特定的行爲,或者你想實現一個 Itera ble,其底層其實是遍歷數據庫查詢的結果集。Guava 爲你,也爲咱們本身提供了若干工具方法,以便讓相似 的工做變得更簡單。(畢竟,咱們本身也要用這些工具擴展集合框架。)java

2、Forwarding裝飾器

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

Forwarding 抽象類定義了一個抽象方法:delegate(),你能夠覆蓋這個方法來返回被裝飾對象。全部其餘方法 都會直接委託給 delegate()。例如說:ForwardingList.get(int)實際上執行了 delegate().get(int)。框架

經過建立 ForwardingXXX 的子類並實現 delegate()方法,能夠選擇性地覆蓋子類的方法來增長裝飾功能,而不 須要本身委託每一個方法——譯者注:由於全部方法都默認委託給 delegate()返回的對象,你能夠只覆蓋須要裝飾 的方法。ide

此外,不少集合方法都對應一個」標準方法[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 並不會改變 Forwardin gMap.putAll 的行爲。當心覆蓋全部須要改變行爲的方法,而且確保裝飾後的集合知足接口契約。code

一般來講,相似於 AbstractList 的抽象集合骨架類,其大多數方法在 Forwarding 裝飾器中都有對應的」標準方 法」實現。對象

對提供特定視圖的接口,Forwarding 裝飾器也爲這些視圖提供了相應的」標準方法」實現。例如,Forwarding Map 提供 StandardKeySet、StandardValues 和 StandardEntrySet 類,它們在能夠的狀況下都會把本身的 方法委託給被裝飾的 Map,把不能委託的聲明爲抽象方法。繼承

3、PeekingIterator

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

Iterators 提供一個 Iterators.peekingIterator(Iterator)方法,來把 Iterator 包裝爲 PeekingIterator,這是 Iterator 的子類,它能讓你事先窺視[peek()]到下一次調用 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 在理解和使用上就比較直接了。

4、AbstractIterator

實現你本身的 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();
			}
		};
	}

你實現了 computeNext()方法,來計算下一個值。若是循環結束了也沒有找到下一個值,請返回 endOfData()代表已經到達迭代的末尾。

注意:AbstractIterator 繼承了 UnmodifiableIterator,因此禁止實現 remove()方法。若是你須要支持 remove()的迭代器,就不該該繼承 AbstractIterator。

AbstractSequentialIterator

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

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

咱們在這兒實現了 computeNext(T)方法,它能接受前一個值做爲參數。

注意,你必須額外傳入一個初始值,或者傳入 null 讓迭代當即結束。由於 computeNext(T)假定 null 值意味着 迭代的末尾——AbstractSequentialIterator 不能用來實現可能返回 null 的迭代器。

相關文章
相關標籤/搜索