Java數據結構漫談-Stack


Stack(棧)是一種比較典型的數據結構,其元素滿足后進先出(LIFO)的特點。

Java中Stack的實現繼承自Vector,所以其天然的具有了一些Vector的特點,所以棧也是線程安全的。

class Stack<E> extends Vector<E> {

事實上,除了繼承自Vector的那些方法之外,Stack只提供了5個方法:

    public E push(E item) {
        addElement(item);

        return item;
    }

    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);
    }

    public boolean empty() {
        return size() == 0;
    }

    public synchronized int search(Object o) {
        int i = lastIndexOf(o);

        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }

push函數是用來向Stack的頂部壓入一個元素,影響其性能的是 addElement的性能:

    public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

可以看出,其方法是在Vector的最后加入一個元素,其時間復雜度是o(1)。

peek函數是從查看棧頂元素,但是不刪除。其性能主要是由Vector的elementAt函數決定的:

    public synchronized E elementAt(int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
        }

        return elementData(index);
    }

    E elementData(int index) {
        return (E) elementData[index];
    }

由於Vector的底層是數組實現的,通過下標可以直接進行定位,所以peek的時間復雜度也是o(1)。

pop函數是移除並獲取到棧頂元素,在源碼中可以看到,這里調用peek獲取了棧頂元素,使用removeElementAt來刪除棧頂元素,這個函數也正是決定pop性能的關鍵:

    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 */
    }

咋一看來,這里刪除一個元素之后,都會對數組中的元素進行復制調整,時間復雜度是o(n),但是考慮到傳進來的index值的特殊性:

index = elementCount -1;

這樣的話if(j>0)的條件永遠都不會成立,因為j永遠都是0,中間復制調整元素的操作就避免了,僅僅是在最后把Vector最后的值賦值為null,時間復雜度是o(1)。

search是查找一個元素在Stack中的index,這里起作用的是Vector的lastIndexOf函數,代碼如下:

    public synchronized int lastIndexOf(Object o) {
        return lastIndexOf(o, elementCount-1);
    }

    public synchronized int lastIndexOf(Object o, int index) {
        if (index >= elementCount)
            throw new IndexOutOfBoundsException(index + " >= "+ elementCount);

        if (o == null) {
            for (int i = index; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = index; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

可以看出,查找的過程是從后向前,挨個比較,其時間復雜度必然是o(n)。

 

Stack是Vector的子類,所以Vector的函數這里也適用,這里不再贅述,在Vector相關的介紹文章中會有。

Stack是線程安全的,所以其性能必然受到影響,如果需要使用一個非線程安全的Stack,可以直接使用LinkedList,LinkedList本身提供的方法就包含了Stack的操作。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM