棧的定義是「限定僅在表頭進行插入和刪除操作的線性表」。初學者常問:既然底層用數組實現,為什麼不能隨意訪問中間元素?本文從抽象數據類型 (ADT) 和封裝角度解答。
「只能在棧頂操作」是棧這一抽象數據類型的規約,而非物理存儲的限制。
push、pop、peek、isEmpty。這些操作天然實現「後進先出」(LIFO),是解決遞歸調用、表達式求值、回溯等問題的精確工具。top 指向棧頂。物理上完全可以讀取任意下標,但那違背了棧的語義。private,僅暴露棧操作方法。使用者無法、也不應繞過接口直接訪問內部數據。public class ArrayStack<E> {
private E[] data;
private int top = -1;
@SuppressWarnings("unchecked")
public ArrayStack(int capacity) {
data = (E[]) new Object[capacity];
}
public void push(E e) {
if (top + 1 == data.length) resize(2 * data.length);
data[++top] = e;
}
public E pop() {
if (isEmpty()) throw new IllegalStateException("Stack empty");
E e = data[top];
data[top--] = null; // 釋放引用
return e;
}
public E peek() {
if (isEmpty()) throw new IllegalStateException("Stack empty");
return data[top];
}
// 故意不提供 get(index)、set(index) 等方法
}
極少數場景(如調試、序列化、特定算法優化)可能需要遍歷棧內元素。此時可:
iterator() 實現 Iterable 接口(僅讀遍歷)。toArray() 返回副本。棧限定只能在棧頂操作,是為了精確建模 LIFO 語義,通過接口封裝保證行為純粹。理解「定義即契約」,才能正確使用各類數據結構。