給jdk寫註釋系列之jdk1.6容器(10)-Stack&Vector源碼解析

  前面咱們已經接觸過幾種數據結構了,有數組、鏈表、Hash表、紅黑樹(二叉查詢樹),今天再來看另一種數據結構:棧。
     什麼是棧呢,我就不找它具體的定義了,直接舉個例子,棧就至關於一個很窄的木桶,咱們往木桶裏放東西,往外拿東西時會發現,咱們最開始放的東西在最底部,最早拿出來的是剛剛放進去的。因此, 棧就是這麼一種先進後出 First In Last Out,或者叫後進先出 的容器,它只有一個口,在這個口放入元素,也在這個口取出元素
 
  棧最主要了兩個動做就是入棧和出棧操做,其實仍是很容易的明白的對不對,那麼咱們接下來就看一下Jdk容器中的棧Stack是怎麼實現的吧。
  
1.定義
1 public
2 class Stack<E> extends Vector<E> {

  咱們發現Stack繼承了Vector,Vector又是什麼東東呢,看一下。html

1 public class Vector<E>
2     extends AbstractList<E>
3     implements List<E>, RandomAccess, Cloneable, java.io.Serializable

  發現沒有,Vector是List的一個實現類,其實Vector也是一個基於數組實現的List容器,其功能及實現代碼和ArrayList基本上是同樣的。那麼不同的是什麼地方的,一個是數組擴容的時候,Vector是*2,ArrayList是*1.5+1;另外一個就是Vector是線程安全的,而ArrayList不是,而Vector線程安全的作法是在每一個方法上面加了一個synchronized關鍵字來保證的。可是這裏說一句,Vector已經不官方的(你們公認的)不被推薦使用了,正式由於其實現線程安全方式是鎖定整個方法,致使的是效率不高,那麼有沒有更好的提到方案呢,其實也不能說有,可是還真就有那麼一個,Collections.synchronizedList(),這不是咱們今天的重點不作深刻探討,回到Stack的實現上。java

  

2.Stack&Vector底層存儲
 
     因爲Stack是繼承和基於Vector,那麼簡單看一下Vector的一些定義和方法源碼:
 1     // 底層使用數組存儲數據
 2     protected Object[] elementData;
 3     // 元素個數
 4     protected int elementCount ;
 5     // 自定義容器擴容遞增大小
 6     protected int capacityIncrement ;
 7 
 8     public Vector( int initialCapacity, int capacityIncrement) {
 9         super();
10         // 越界檢查
11         if (initialCapacity < 0)
12             throw new IllegalArgumentException( "Illegal Capacity: " +
13                                                initialCapacity);
14         // 初始化數組
15         this.elementData = new Object[initialCapacity];
16         this.capacityIncrement = capacityIncrement;
17     }
18  
19     // 使用synchronized關鍵字鎖定方法,保證同一時間內只有一個線程能夠操縱該方法
20     public synchronized boolean add(E e) {
21         modCount++;
22        // 擴容檢查
23        ensureCapacityHelper( elementCount + 1);
24         elementData[elementCount ++] = e;
25         return true;
26     }
27  
28     private void ensureCapacityHelper(int minCapacity) {
29         // 當前元素數量
30         int oldCapacity = elementData .length;
31         // 是否須要擴容
32         if (minCapacity > oldCapacity) {
33            Object[] oldData = elementData;
34            // 若是自定義了容器擴容遞增大小,則按照capacityIncrement進行擴容,不然按兩倍進行擴容(*2)
35            int newCapacity = (capacityIncrement > 0) ?
36               (oldCapacity + capacityIncrement) : (oldCapacity * 2);
37            if (newCapacity < minCapacity) {
38               newCapacity = minCapacity;
39            }
40            // 數組copy
41             elementData = Arrays.copyOf( elementData, newCapacity);
42        }
43     }
  Vector就簡單看到這裏,其餘方法Stack若是沒有調用的話就不進行分析了,不明白的能夠去看ArrayList源碼解析。
 
3.peek()——獲取棧頂的對象
 1     /**
 2      * 獲取棧頂的對象,可是不刪除
 3      */
 4     public synchronized E peek() {
 5         // 當前容器元素個數
 6         int   len = size();
 7      
 8         // 若是沒有元素,則直接拋出異常
 9         if (len == 0)
10            throw new EmptyStackException();
11         // 調用elementAt方法取出數組最後一個元素(最後一個元素在棧頂)
12         return elementAt(len - 1);
13     }
14  
15     /**
16      * 根據index索引取出該位置的元素,這個方法在Vector中
17      */
18     public synchronized E elementAt(int index) {
19         // 越界檢查
20         if (index >= elementCount ) {
21            throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
22        }
23 
24         // 直接經過數組下標獲取元素
25         return (E)elementData [index];
26     }

 

4.pop()——彈棧(出棧),獲取棧頂的對象,並將該對象從容器中刪除數組

 1     /**
 2      * 彈棧,獲取並刪除棧頂的對象
 3      */
 4     public synchronized E pop() {
 5         // 記錄棧頂的對象
 6        E      obj;
 7         // 當前容器元素個數
 8         int   len = size();
 9 
10        // 經過peek()方法獲取棧頂對象
11        obj = peek();
12        // 調用removeElement方法刪除棧頂對象
13        removeElementAt(len - 1);
14 
15        // 返回棧頂對象
16         return obj;
17     }
18 
19     /**
20      * 根據index索引刪除元素
21      */
22     public synchronized void removeElementAt(int index) {
23         modCount++;
24         // 越界檢查
25         if (index >= elementCount ) {
26            throw new ArrayIndexOutOfBoundsException(index + " >= " +
27                                               elementCount);
28        }
29         else if (index < 0) {
30            throw new ArrayIndexOutOfBoundsException(index);
31        }
32         // 計算數組元素要移動的個數
33         int j = elementCount - index - 1;
34         if (j > 0) {
35            // 進行數組移動,中間刪除了一個,因此將後面的元素往前移動(這裏直接移動將index位置元素覆蓋掉,就至關於刪除了)
36            System. arraycopy(elementData, index + 1, elementData, index, j);
37        }
38         // 容器元素個數減1
39         elementCount--;
40         // 將容器最後一個元素置空(由於刪除了一個元素,而後index後面的元素都向前移動了,因此最後一個就沒用了 )
41         elementData[elementCount ] = null; /* to let gc do its work */
42     }

 

5.push(E item)——壓棧(入棧),將對象添加進容器並返回安全

 1     /**
 2      * 將對象添加進容器並返回
 3      */
 4     public E push(E item) {
 5        // 調用addElement將元素添加進容器
 6        addElement(item);
 7        // 返回該元素
 8         return item;
 9     }
10 
11     /**
12      * 將元素添加進容器,這個方法在Vector中
13      */
14     public synchronized void addElement(E obj) {
15         modCount++;
16        // 擴容檢查
17        ensureCapacityHelper( elementCount + 1);
18        // 將對象放入到數組中,元素個數+1
19         elementData[elementCount ++] = obj;
20     }

 

6.search(Object o)——返回對象在容器中的位置,棧頂爲1數據結構

 1     /**
 2      * 返回對象在容器中的位置,棧頂爲1
 3      */
 4     public synchronized int search(Object o) {
 5         // 從數組中查找元素,從最後一次出現
 6         int i = lastIndexOf(o);
 7 
 8         // 由於棧頂算1,因此要用size()-i計算
 9         if (i >= 0) {
10            return size() - i;
11        }
12         return -1;
13     }

 

7.empty()——容器是否爲空dom

1     /**
2      * 檢查容器是否爲空
3      */
4     public boolean empty() {
5         return size() == 0;
6     }
  到這裏Stack的方法就分析完成了,因爲Stack最終仍是基於數組的,理解起來仍是很容易的(由於有了ArrayList的基礎啦)。
 
 
 
     雖然jdk中Stack的源碼分析完了,可是這裏有必要討論下,不知道是否發現這裏的Stack很奇怪的現象,
     (1)Stack爲何是基於數組實現的呢?
          咱們都知道數組的特色:方便根據下標查詢(隨機訪問),可是內存固定,且擴容效率較低。很容易想到Stack用鏈表實現最合適的。
     (2)Stack爲何是繼承Vector的?
          繼承也就意味着Stack繼承了Vector的方法,這使得Stack有點不三不四的感受,既是List又是Stack。若是非要繼承Vector合理的作法應該是什麼:Stack不繼承Vector,而只是在自身有一個Vector的引用,聚合對不對?
 
     惟一的解釋呢,就是Stack是jdk1.0出來的,那個時候jdk中的容器尚未ArrayList、LinkedList等只有Vector,既然已經有了Vector且能實現Stack的功能,那麼就幹吧。。。
 
     既然用鏈表實現Stack是比較理想的,那麼咱們就來嘗試一下吧:
 1 import java.util.LinkedList;
 2 
 3 public class LinkedStack<E> {
 4 
 5         private LinkedList<E> linked ;
 6 
 7         public LinkedStack() {
 8                this.linked = new LinkedList<E>();
 9        }
10 
11         public E push(E item) {
12                this.linked .addFirst(item);
13                return item;
14        }
15 
16         public E pop() {
17                if (this.linked.isEmpty()) {
18                       return null;
19               }
20                return this.linked.removeFirst();
21        }
22 
23         public E peek() {
24                if (this.linked.isEmpty()) {
25                       return null;
26               }
27                return this.linked.getFirst();
28        }
29        
30         public int search(E item) {
31                int i = this.linked.indexOf(item);
32                return i + 1;
33        }
34        
35         public boolean empty() {
36                return this.linked.isEmpty();
37        }
38 }
  看完後,你說我cha,爲何這麼簡單,就這麼點代碼。由於這裏使用的LinkedList實現的Stack,記得在LinkedList中說過,LinkedList實現了Deque接口使得它既能夠做爲棧(先進後出),又能夠做爲隊列(先進先出)。那麼什麼是隊列呢?Queue見吧!    
 
     Stack&Vector 完! 
 
 
參見:
相關文章
相關標籤/搜索