1. 程式人生 > >從JDK裡Stack原始碼的角度重溫棧的實現

從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}