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的操作。