在前幾節(第 38 至 51 節)中,我們詳細介紹了各種具體的容器實現類,如 ArrayList、LinkedList、HashMap 等。這些類並非從零開始構建,而是繼承自一系列抽象容器類。這些抽象類提供了容器接口的部分默認實現,極大地簡化了具體容器類的開發工作。理解它們的設計邏輯,不僅有助於深入掌握現有容器的內部機制,更能指導我們如何正確擴展容器框架。
實現標準容器接口(如 Collection、List、Map)主要有兩大核心優勢:
java.util.Collections 工具類提供了大量針對容器接口的通用算法(排序、查找、同步包裝等),實現接口即可「拿來即用」,無需重複造輪子。Java 容器框架中共有六大核心抽象類,它們與接口及具體類的繼承關係如下:
Collection 接口,是 AbstractList、AbstractSet、AbstractQueue 的父類(ArrayDeque 亦繼承此類)。AbstractCollection,實現 List 接口,是 ArrayList、AbstractSequentialList 的父類。AbstractList,專為順序訪問結構(如鏈表)設計,LinkedList 繼承自此類。AbstractCollection,實現 Set 接口,HashSet、TreeSet、EnumSet 均繼承此類。AbstractCollection,實現 Queue 接口,PriorityQueue 繼承此類。Map 接口,HashMap、TreeMap、EnumMap 等繼承此類。AbstractCollection 提供了 Collection 接口的基礎實現,覆蓋了除 size()、iterator()、add(E e) 之外的所有方法。其核心設計思想是模板方法模式:將複雜操作(如 addAll、containsAll、retainAll)委託給基本操作(iterator()、add、size)。
關鍵點:
size() 和 iterator() 為抽象方法,子類必須實現。add(E e) 默認拋出 UnsupportedOperationException,不可變集合可直接使用,可變集合必須重寫。contains 通過遍歷迭代器比較元素,retainAll 通過迭代器的 remove() 移除不在指定集合中的元素。擴展實戰:基於動態數組實現 MyCollection
public class MyCollection<E> extends AbstractCollection<E> {
private DynamicArray<E> darr = new DynamicArray<>();
public MyCollection() {}
public MyCollection(Collection<? extends E> c) { addAll(c); }
@Override public Iterator<E> iterator() { return new DynamicArrayIterator<>(darr); }
@Override public int size() { return darr.size(); }
@Override public boolean add(E e) { darr.add(e); return true; }
}
只需實現三個核心方法(size、iterator、add)並提供兩個標準構造器,即可獲得完整的 Collection 功能。
AbstractList 針對 List 接口提供了豐富的默認實現,包括 addAll(int, Collection)、equals、hashCode、indexOf、listIterator、subList 等。其核心依賴四個基本操作:size()、get(int)、set(int, E)、add(int, E)、remove(int)。
設計假設:假定子類支持高效的隨機索引訪問(如數組結構)。迭代器內部基於 get(int) 實現,listIterator 同理。
擴展實戰:基於動態數組實現 MyList
public class MyList<E> extends AbstractList<E> {
private DynamicArray<E> darr = new DynamicArray<>();
public MyList() {}
public MyList(Collection<? extends E> c) { addAll(c); }
@Override public E get(int index) { return darr.get(index); }
@Override public int size() { return darr.size(); }
@Override public E set(int index, E element) { return darr.set(index, element); }
@Override public void add(int index, E element) { darr.add(index, element); }
@Override public E remove(int index) { return darr.remove(index); }
}
雖然繼承自 AbstractList,但 AbstractSequentialList 徹底反轉了實現邏輯:它要求子類實現 listIterator(int),而將 get、set、add、remove 等索引操作構建在迭代器之上。這使其成為鏈表等順序訪問結構的理想基類(LinkedList 即繳此類)。
核心對比:
| 特性 | AbstractList | AbstractSequentialList |
|---|---|---|
| 子類必須實現 | get/set/add/remove (索引操作) | listIterator(int) (迭代器) |
| 索引操作實現 | 直接實現 | 基於迭代器實現 |
| 迭代器實現 | 基於索引操作 | 直接實現 |
| 適用結構 | 隨機訪問 (數組) | 順序訪問 (鏈表) |
擴展實戰:基於動態數組實現 MySeqList (演示用途)
public class MySeqList<E> extends AbstractSequentialList<E> {
private DynamicArray<E> darr = new DynamicArray<>();
public MySeqList() {}
public MySeqList(Collection<? extends E> c) { addAll(c); }
@Override public ListIterator<E> listIterator(int index) {
return new DynamicArrayListIterator<>(index, darr);
}
@Override public int size() { return darr.size(); }
}
AbstractMap 實現了 Map 大部分方法,核心依賴兩個基本操作:put(K, V) 和抽象方法 entrySet()。所有查詢操作(containsKey、get、keySet、values 等)均基於 entrySet() 返回的鍵值對集合視圖實現。
關鍵設計:
remove(Object key) 通過 entrySet().iterator().remove() 實現,因此若 Map 可修改,entrySet 的迭代器必須支持 remove()。SimpleEntry (可變) 和 SimpleImmutableEntry (不可變) 兩種鍵值對實現。擴展實戰:基於動態數組實現 MyMap
public class MyMap<K, V> extends AbstractMap<K, V> {
private DynamicArray<Map.Entry<K, V>> darr = new DynamicArray<>();
private Set<Map.Entry<K, V>> entrySet;
public MyMap() {}
public MyMap(Map<? extends K, ? extends V> m) { putAll(m); }
@Override public V put(K key, V value) {
for (int i = 0; i < darr.size(); i++) {
Map.Entry<K, V> entry = darr.get(i);
if (keyEquals(key, entry.getKey())) {
V old = entry.getValue();
entry.setValue(value);
return old;
}
}
darr.add(new AbstractMap.SimpleEntry<>(key, value));
return null;
}
@Override public Set<Entry<K, V>> entrySet() {
if (entrySet == null) entrySet = new EntrySet();
return entrySet;
}
private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
@Override public Iterator<Map.Entry<K, V>> iterator() {
return new DynamicArrayIterator<>(darr); // 支持 remove
}
@Override public int size() { return darr.size(); }
}
}
AbstractCollection,僅添加了 equals 和 hashCode 的語義正確實現(基於元素內容而非引用)。擴展時需自行保證「無重複元素」約束(如在 add 中檢查)。AbstractCollection,實現了 add、addAll、clear、element、remove 等方法,均委託給 offer、peek、poll 三個核心隊列操作。擴展時只需實現這三個方法。本節系統梳理了 Java 容器框架的六大抽象基類,揭示了它們「以少勝多」的模板方法設計精髓:
AbstractCollection 以 iterator() + size() + add() 構建完整集合功能AbstractList 以隨機索引操作為核心,適配數組類結構AbstractSequentialList 以迭代器為核心,適配鏈表類結構AbstractMap 以 entrySet() 為核心視圖構建映射功能掌握這些抽象類,不僅能讀懂 JDK 容器源碼,更能在需要自定義容器時(如特殊數據結構、領域模型集成)事半功倍。下一節,我們將探討 Collections 工具類中那些強大的通用算法,看看實現了標準接口後能獲得哪些「開箱即用」的能力。
未完待續,關注「老馬說編程」獲取最新文章,從入門到高級,深入淺出探索 Java 本質。
