Java 1.0/1.1 提供的集合類雖然已被 Java 2 Collections Framework(List/Set/Map 接口體系)取代,但在存量代碼、面試考察、理解演進歷史方面仍具重要價值。本文深度解析 Vector、BitSet、Stack、Hashtable/Dictionary 及 Enumeration 的原理、源碼陷阱與現代替代方案。
List 接口,底層 Object[] elementData,容量自動增長(默認 2 倍)。addElement()、elementAt()、elements() 返回 Enumeration。
public class CrashJava {
public String toString() {
return "CrashJava address: " + this + "\n"; // this 觸發 toString 遞歸
}
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 10; i++) v.addElement(new CrashJava());
System.out.println(v); // Vector.toString() 遍歷調用元素 toString() → StackOverflowError
}
}
根因:"str" + this 隱式調用 String.valueOf(this) → this.toString() 形成無限遞歸。
修正:super.toString() 獲取 ClassName@hashCode,或顯式 Integer.toHexString(hashCode())。
ArrayList(非同步、擴容 1.5 倍)。Collections.synchronizedList(new ArrayList()) 或 CopyOnWriteArrayList(讀多寫少)。long[] words 存儲(每 64 位一塊)。int/long 位運算高效。
BitSet bs = new BitSet();
bs.set(100); // 置 1
bs.clear(100); // 置 0
bs.get(100); // 讀取
bs.flip(100); // 取反
bs.cardinality(); // 統計 1 的個數
bs.and(other); // 位與(原地修改)
bs.or(other); // 位或
bs.xor(other); // 位異或
bs.andNot(other); // 位與非
早期版本 set(index) 若 index > 當前容量*2 會拋出 ArrayIndexOutOfBoundsException,需先 set(中間索引) 觸發擴容。Java 1.1 已修復,現代版本自動無縫擴容。
Stack extends Vector 違反「組合優於繼承」原則,暴露 addElement()、elementAt() 等非棧語義方法。
Stack<String> st = new Stack<>();
st.push("A"); st.push("B"); st.push("C");
st.peek(); // "C" 不移除
st.pop(); // "C" 移除
st.empty(); // false
st.search("A"); // 從頂部算起的 1-based 索引,返回 3
ArrayDeque(非同步、雙端、性能最優)、LinkedList(同樣實現 Deque)。Deque<E> stack = new ArrayDeque<>();put/get/remove/size/isEmpty/keys/elements。Hashtable 一個 JDK 實現。Entry<K,V>[] table 數組 + 鏈表解決衝突(Java 7 以前無樹化)。Enumeration<K> keys()、Enumeration<V> elements() 返回枚舉器。
Hashtable<Integer, Counter> ht = new Hashtable<>();
for (int i = 0; i < 10000; i++) {
Integer r = (int)(Math.random() * 20);
ht.computeIfAbsent(r, k -> new Counter()).increment();
}
System.out.println(ht); // {19=489, 18=510, ...}
Counter 為可變對象封裝 int count,因基本類型不可放入集合且包裝類不可變。
class Groundhog {
int id;
Groundhog(int n) { id = n; }
// 錯誤:繼承 Object 默認 hashCode=地址,equals=地址比較
// 導致 new Groundhog(3) 無法命中 ht.put(new Groundhog(3), ...)
}
class Groundhog2 {
int id;
Groundhog2(int n) { id = n; }
@Override public int hashCode() { return id; }
@Override public boolean equals(Object o) {
return o instanceof Groundhog2 && id == ((Groundhog2)o).id;
}
}
鐵律:equals(true) ⇒ hashCode() 相等;hashCode() 不等 ⇒ equals(false)。違反將導致 HashMap/Hashtable/HashSet 失效。
defaults 字段)。getProperty(key) 先查主表,再查默認表。load/store 支持流式讀寫 .properties 文件(ISO-8859-1 編碼,中文需轉義)。
public interface Enumeration<E> {
boolean hasMoreElements();
E nextElement();
}
static void print(Enumeration<?> e) {
while (e.hasMoreElements())
System.out.println(e.nextElement().toString());
}
// 同一打印邏輯複用 Vector、Hashtable、自定義集合
Vector<Mouse> vm = ...;
Hashtable<Integer, Hamster> hm = ...;
print(vm.elements());
print(hm.elements());
Enumeration 隱藏底層數據結構(數組/鏈表/哈希表),實現「算法與容器分離」,是 Iterator 模式前身。
| 特性 | Enumeration | Iterator |
|---|---|---|
| 移除支持 | 無 | remove() |
| 方法名 | hasMoreElements/nextElement | hasNext/next |
| Fail-fast | 無 | 有(modCount 機制) |
| for-each 支持 | 無 | 有(實現 Iterable) |
| 舊類 | 現代替代 | 遷移要點 |
|---|---|---|
| Vector | ArrayList / CopyOnWriteArrayList | 去同步、擴容因子 1.5、實現 List 接口 |
| Stack | ArrayDeque (as Deque) | 組合優於繼承、雙端操作、非同步 |
| Hashtable | HashMap / ConcurrentHashMap | 允許 null、非同步/分段鎖、性能數量級提升 |
| Enumeration | Iterator / Spliterator | 支持移除、fail-fast、流式操作、for-each |
| BitSet | BitSet (保留) | 位運算場景無替代品,Java 8+ 新增 stream()、toLongArray() |
理解早期集合設計權衡(同步 vs 性能、繼承 vs 組合、枚舉 vs 迭代器),有助於在現代框架選型、性能調優、舊系統重構中做出更明智決策。