3.2 表 ADT -3.3 Java Collection API 中的表
3.2 表 ADT
處理形如 A0, A1, A2, ……, AN-1 的一般的表。我們稱這個表大小為N。將大小為0的特殊表稱為空表
對於除空表以外的任何表,稱 Ai-1 前驅 Ai,Ai 後繼 Ai-1。
表ADT上進行操作有:
printList 列印整個表
makeEmpty 清空整個表
find 返回某一項首次出現的位置
insert 從表的某個位置插入元素
remove 從表的某個位置刪除元素
findKth 返回某個位置上的元素
陣列實現表
對錶的操作都可以通過陣列來實現。雖然陣列由固定容量建立,但是需要的時候可以使用雙倍的容量再建立一個數組。
在這種實現下,printList花費時間為 O(N),findKth 花費時間是常數。但是插入和刪除也會花費O(N)的時間,因為在 0 位置插入或刪除,會將陣列元素整體後移或前移。平均下來都需要移動表的一半的元素。
表的另一種實現方式:連結串列
連結串列由一系列節點組成,每個節點均含有表元素和到包含該元素後繼的節點的鏈。鏈只有後繼元素的資訊,並沒有前驅節點的資訊。
和陣列實現一樣,printList 花費時間為 O(N),findKth(i) 花費時間 O(i)。remove 方法可以通過修改一個 next 引用來實現,insert 則需要改變兩個next 引用。
實際中更為常用的是雙鏈表。
3.3 Java Collections API 中的表
Collection介面
public interface Collection<E> extends Iterable<E> { int size(); boolean isEmpty(); void clear(); boolean contains(Object o); boolean add(E e); boolean remove(E e); Iterator<E> iterator(); }
Collection 實現了 Iterable 介面,因此可以對其使用增強 for 迴圈
public static <E> void enhanceFor(Collection<E> coll) {
for (E e : coll) {
//對每一個元素操作
}
}
Iterator介面
public interface Iterator<E> { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationException("remove"); }
通過 iterato r方法,每個集合都可以建立並返回一個實現 Iterator 的物件
next 方法返回下一項,hasNext 方法返回是否存在下一項。編譯器遇見一個用於 Iterable 物件的增強 for 迴圈時,它用 iterator 方法的方法來替代增強 for 迴圈。如下
public static <E> void iter(Collection<E> coll) {
Iterator<E> iter = coll.iterator();
while (iter.hasNext()) {
E item = iter.next();
//對每一個元素操作
}
}
remove 方法將刪除由 next 最新返回的元素。Collection 的 remove 方法必須先找出要被刪除的項。而 Iterator 的 remove 方法要刪除的就是當前迭代器所在位置的元素。
當直接使用 Iterator 而不是通過一個增強 for 迴圈間接使用時,如果對正在被迭代的集合進行結構上的改變( 即對該集合使用 add 、remove 或 clear 方法), 那麼迭代器就不再合法。
只有在需要立即使用一個迭代器的時候,才應該獲取迭代器。如果迭代器呼叫了自己的 remove 方法,那麼這個迭代器依然合法,且自己的 remove 方法也只應該被呼叫一次。
List 介面、ArrayList 類和 LinkedList 類
List 介面繼承了 Collection 介面
public interface List<E> extends Collection<E> {
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
}
listIterator 方法將產生比通常認為還要複雜的迭代器。
ArrayList 類提供了 List ADT 的一種可增長陣列的實現,對 get 和 set 的呼叫花費常數時間。缺點是新項插入和現有項刪除代價高。LinkedList 類提供的是 List ADT 的雙鏈表實現,優點是新項的插入和刪除都是常數時間的操作,這裡假設變動項的位置是已知的。LinkedList 對 get 的呼叫代價高。
不論 ArrayList 或是 LinkedList 作為引數被傳遞,makeList1 的執行時間都是 O(N) ,因為每次對add呼叫都是在表的末端。從而均花費常數時間(忽略ArrayList擴容的時間)
public static void makeList1(List<Integer> lst, int N) {
lst.clear();
for (int i = 0; i < N; i++) {
lst.add(i);
}
}
對於 LinkedList ,makeList2 的執行時間還是 O(N),而對於 ArrayList 執行時間是 O(N2),因為在 ArrayList 中在前端新增是一個 O(N) 操作。
public static void makeList2(List<Integer> lst, int N) {
lst.clear();
for (int i = 0; i < N; i++) {
lst.add(0,i);
}
}
ListIterator 類
擴充套件了 List 的 Iterator 的功能。方法 previous 和 hasPrevious 能夠使表從後向前遍歷
public interface ListIterator<E> extends Iterator<E> {
boolean hasPrevious();
E previous();
}
add 方法將一個新的項以當前位置放入表中,set 方法改變被迭代器看到的最後一個值,對於 LinkedList 來說很方便