從JDK裡Stack原始碼的角度重溫棧的實現
棧概念
棧是元素的集合, 其中有兩個原則操作:
- push, 它新增到集合中
- pop 則移除最近新增的元素
Push - 新增元素到棧裡
下面是push,pop相關的幾個關鍵方法的原始碼
public E push(E item) {
addElement(item);
return item;
}
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0 ) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
push方法呼叫addElement(item)往棧裡新增元素,elementData是一個object陣列,addElement方法做的一件事就是把obj元素新增到elementData數組裡,陣列的長度是固定的,
不斷新增元素肯定會超陣列的容量,ensureCapacityHelper這個方法就是確認陣列的容量
是否足夠,不夠就進行擴充。接著看grow這個方法,進行擴充,oldCapacity變數記錄舊的陣列長度,newCapacity等於oldCapacity加上一個增量,capacityIncrement變數是增量,但預設為0,可以在構造器中賦值,capacityIncrement為0時,增量等於oldCapacity,newCapacity相當於增加了一倍。最後呼叫Arrays的copyOf方法把原來的elementData陣列複製到一個新的數組裡。
Pop - 移除最近新增的元素
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
pop方法:移除並返回移除棧頂的元素
peek方法: 只是返回棧頂的元素
pop方法是先呼叫peek方法獲取棧頂的元素,在呼叫removeElementAt方法移除。
先看peek方法, 陣列的index是從0開始,棧頂的index就是len - 1, 最終實質是返回elementData[index]即可。
接著看removeElementAt方法的實現
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
這個方法呼叫System.arraycopy()進行復制資料.
這個方法是native方法, 先了解一下這個方法的引數,
/**
* src : 原陣列
* srcPos : 原陣列起始位置
* dest :目標陣列
* destPos : 目標陣列起始位置
* length : 複製陣列的長度
**/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
j = 陣列長度- index(需要移除的位置) - 1;
例如有{ a, b, c, d ,e } 共5個元素, 要移除c, 這裡 j 等於 c 之後的元素個數,即2個.
呼叫System.arraycopy方法時,原陣列和目標陣列都是同一個,實質上是把d,e 複製到原來c, d 的位置上,目標陣列為{ a, b, d, e, e}
最後elementCount減1,把陣列elementData最後一個賦值null, 最終陣列為{ a, b, d, e, null}