Java核心技術:集合——Java集合框架


Java最初版本只爲最常用的數據結構提供了很少一組類:Vector、Stack、Hashtable、BitSet與Enumeration接口,其中的Enumeration接口提供了一種用於訪問任意容器中各個元素的抽象機制。
Java SE 1.2之後,設計人員退出了一組功能更加完善的數據結構。

將集合的接口與實現分離

Java集合類庫將接口(interface)與實現(implementation)分離。下面以隊列(queue)進行舉例說明。
隊列結構指出了隊列可以進行的操作,隊列接口的最簡形式可能類似下面這樣:

public interface Queue<E> // a simplified from of the interface in the standard library
{
	void add(E element);
	E remove();
	int size();
}

隊列通常有兩種實現方式:一種使用循環數組;另一種使用鏈表。

public class CircularArrayQueue<E> implements Queue<E> // not an actual library class
{
	private int head;
	private int tail;

	CircularArrayQueue(int capacity) { ... }
	public void add(E element) { ... }
	public E remove() { ... }
	public int size() { ... }
	private E[] elements;
}
public class LinkedListQueue<E> implements Queue<E> // not an actual library class
{
	private Link head;
	private Link tail;

	LinkedListQueue() { ... }
	public void add(E element) { ... }
	public E remove() { ... }
	public int size() { ... }
}

註釋:Java類庫並沒有這兩個類。這裏,只是作爲示例。
當在程序中使用隊列時,一旦集合構建後就不需要知道究竟使用了哪種實現。因此,只有在構建集合對象時,使用具體的類纔有意義。可以使用接口類型存放集合的引用

Queue<Customer> expressLane = new CircularArrayQueue<>(100);
expressLane.add(new Customer("Harray"));

利用這種方式,一旦改變了想法,可以輕鬆地使用另一種不同的實現。只需要對程序調用構建的地方做出修改。如果覺得LinkedListQueue是個更好的選擇,就將代碼修改爲:

Queue<Customer> expressLane = new LinkedListQueue<>();
expressLane.add(new Customer("Harray"));

在研究API文檔時,會發現另一組名字已Absract開頭的類,例如,AbstractQueue。這些類是爲類庫實現着而設計的。擴展這些類要比實現接口中的所有方法輕鬆得多。

Collection接口

在Java類庫中,集合類的基本接口是Collection接口。

public interface Collection<E>
{
	boolean add(E element);
	Iterator<E> iterator();
	...
}

迭代器

Iteratro接口包含4個方法:

public interface Iterator<E>
{
	E next();
	boolean hasNext();
	void remove();
	default void forEachRemaining(Consumer<? super E> action);
}

通過反覆調用next方法,可以逐個訪問集合中的每個元素。但是,如果到達了集合的末尾,next方法將拋出一個NoSuchElementException。因此,需要在調用next之前調用hasNext方法。如果迭代器對象還有多個供訪問的元素,這個方法就返回true。

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while(iter.hasNext())
{
	String element = iter.next();
	do something with element
}

用“for each”循環可以更加簡練。

for (String element : c)
{
	do something with element
}

編譯器簡單地將“for each”循環翻譯爲帶有迭代器的循環
“for each”循環可以與任何實現了Iterable接口的對象一起工作,這個接口只包含一個抽象方法:

public interface Iterable<E>
{
	Iterator<E> iterator();
}

Collection接口擴展了Iterable接口。對於標準類庫的任何集合都可以用"for each"循環。
在Java SE 8中,甚至不用寫循環。可以調用forEachRemaining方法並提供一個lambda表達式。將對迭代器的每一個元素調用這個lambda表達式,知道再沒有元素爲止。

iterator.forEachRemaining(elment -> do something with element);

應該將Java迭代器認爲是位於兩個元素之間。當調用next時,迭代器就越過下一個元素,並返回剛剛越過的那個元素的引用。
迭代器
可以將Iterator.nextInputStream.read看作爲等效的。
Iterator接口的remove方法將會刪除上次調用next方法時返回的元素。如果要刪除指定位置上的元素,仍然需要越過這個元素。

Iterator<String> it = c.iterator();
it.next();
it.remove();

對next方法和remove方法的調用具有互相依賴性。如果調用remove之前沒有調用next將是不合法的。不然,會拋出一個IllegalStateException異常。

泛型使用方法

Collection與Iterator都是泛型接口,可以編寫操作任何集合類型的方法。
Collection接口聲明瞭很多有用的方法。所有的實現類都必須提供這些方法。

int size()
boolean isEmpty()
boolean contains(Object obj)
boolean equals((Object obj)
boolean addAll(Collection<? extends E> from)
boolean remove(Object obj)
boolean removeAll(Collection<?> c)
void clear()
boolean retainAll(Collection<?> c)
Object[] toArray()
<T> T[] toArray(T[] arrayToFill)

爲了能夠讓實現者更容易地實現這個接口,Java類庫提供了一個類AbstractCollection,一個具體的集合類可以擴展AbstractCollection類,AbstractCollection抽象了size和iterator方法。
Collection
Iterator

集合框架中的接口

集合框架中的接口
集合有兩個基本接口:Collection和Map
List是一個有序集合(ordered collection)。支持使用迭代器訪問或者使用一個整數索引訪問。
由數組支持的有序集合可以快速地隨機訪問,鏈表也是有序的,但隨機訪問很慢,最好用迭代器來遍歷。Java的集合框架並沒有使用不同的接口進行區分,爲了避免對鏈表完成隨機訪問操作,Java SE 1.4引入了一個標記接口RandomAccess。這個接口不包含任何方法,不過可以用來測試一個特定的集合是否支持高效的隨機訪問:

if (c instanceof RandomAccess)
{
	use random access algorithm
}
else
{
	use sequential access algorithm
}

Set接口等同於Collection,不過add方法不允許增加重複的元素。要適當定義集(set)的equals方法:只要兩個集包含同樣的元素就認爲是相等的,而不要求這些元素有同樣的順序。hashCode方法的定義要保證包含相同元素的兩個集會得到相同的散列碼。
SortedSet和SortedMap接口會提供用於排序的比較器對象。
Java SE 6引入了接口NavigableSet和NavigableMap,包含一些用於搜索和遍歷有序集合映射的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章