Java-集合類源碼List篇(三)

前言

  前面分析了ArrayList和LinkedList的實現,分別是基於數組和雙向鏈表的List實現。但看之前那張圖,還有兩個實現類,一個是Vector,另一個是Stack,接下里一起走進它們的源碼世界吧!

4. Vector

Vector跟ArrayList比較相似,繼承實現的類或者接口也都是一樣的,都是繼承自AbstractList,同時底層也是基於數組來實現的。

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

4.1 Vector成員變量

/**
     * 數組來保存集合中的元素
     */
    protected Object[] elementData;

    /**
     * 集合size
     */
    protected int elementCount;

    /**
     * 增長步長
     */
    protected int capacityIncrement;

Vector既然是基於數組來實現的,那麼肯定有一個數組的成員變量elementData,返回集合中當前元素的個數elementCount。與ArrayList不同的是增加了一個增長步長,我們知道在ArrayList中,每次增長是上一次的容量的1.5倍,而Vector中則是如果指定了capacityIncrement且該值爲正整數,則每次需要擴容時的容量增長爲該數值。

4.2 Vector訪問元素

 /**
     * 添加元素
     */
    public synchronized boolean add(E e) {
        modCount++;
        //判斷是否需要擴容
        ensureCapacityHelper(elementCount + 1);
        //將元素添加至指定位置
        elementData[elementCount++] = e;
        return true;
    }

看到上述方法是否很熟悉,沒錯,它其實跟ArrayList的添加元素方法基本差不多。唯一不同的就是該方法加了synchronized關鍵字,該關鍵字的意思就是訪問該方法時需要獲取到對象鎖的線程方能訪問。這就是Vector的線程安全訪問機制,通過爲每個方法添加synchronized關鍵字的方法達到在多線程對集合類進行操作時,能夠線程安全訪問。其它的基本與ArrayList無異。

5. Stack

 看Vector的成員變量的時候,發現都是protected修飾的,所以是可以被子類直接訪問的。

public
class Stack<E> extends Vector<E> 

的確在Stack中是沒有另外成員變量的,與Vector相比只不過增加了對棧操作的方法,所以Stack是基於數組的線程安全的棧實現集合。

 

Stack是一個基於LILO(後進先出)的棧的實現,它初始化的時候也是一個空的棧。可以看下他的構造函數:

 /**
     * Creates an empty Stack.
     */
    public Stack() {
    }

5.1 Stack的成員方法

public E push(E item);

添加元素(入棧)

public synchronized E pop();

移除元素(出棧)

public synchronized E peek();

返回棧頂元素

public synchronized int search(Object o);

查找元素

看到上述的成員方法,就知道它的方法同Vector一樣都是同步的,所以它也是一種線程安全的實現。上面5個方法中,push是沒有synchronized修飾的,而其他四個方法都是被synchronized修飾,這個是爲什麼呢?我們來看下push的源碼實現:

   public E push(E item) {
        //調用Vector中的實現
        addElement(item);

        return item;
    }

    /**
     * Vector中的addElement實現
     * @param obj
     */
    public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

所以我們知道,push()方法其實調用的還是父類Vector的實現,所以就沒有加synchronized修飾也能達到同步的效果啦!

 

再來看下search()方法實現:

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

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

返回對象在棧中的位置,下標從1開始,棧頂元素爲1。 

其中數組的最後一位元素即爲棧頂元素,search方法返回的是離棧頂最近的item與棧頂之間的距離!

在JDK中,有如下語句:

* <p>A more complete and consistent set of LIFO stack operations is
 * provided by the {@link Deque} interface and its implementations, which
 * should be used in preference to this class.  For example:
 * <pre>   {@code
 *   Deque<Integer> stack = new ArrayDeque<Integer>();}</pre>
 *

如果想要實現一個LIFO的基於數組棧實現的話,JDK中提供了ArrayDeque是應該優先考慮的。事實上ArrayDeque同LinkedList一樣,都是可以作爲LIFO(後進先出)的棧實現,同時也是可以作爲FIFO(先進先出)的隊列實現。那麼就有個問題了:

爲什麼同樣基於數組實現,Stack只能實現棧而ArrayDeque卻可以作爲棧和隊列的實現呢?

要想實現FIFO的隊列實現,那麼必須滿足能夠在隊列首部出隊列,尾部進隊列,就是要兩端能夠操作。

假定有以上數組,那麼如果基於棧實現的話,則只需要維護該數組的尾端,出棧移除最大索引元素,size--,入棧則只需要判斷size+1與length的大小,看是否需要擴容。如果要做到隊首進行出隊列,隊尾入隊列的操作,則每次出隊列後,爲保持數組空間的連續性都需要將全部元素往前移動一位,那麼效率上則不是一種很理想的實現。

 

事實上,ArrayDeque是雖然也是採用基於數組實現的,而它的則增加了邏輯上的循環,通過增加head和tail使其變成了“循環數組”。所以在ArrayDeque中尾部指向索引tail不一定比首部索引head更大。至於具體實現就到後面Deque的接口實現類部分再詳述啦!

 

小結

本篇中,主要是分析了基於的數組的集合實現類Vector和Stack,下面小結一下:

Stack與LinkedList用作堆棧時異同:


1.實現機制:Stack是基於數組實現的,LinkedList是基於鏈表實現的。

2.線程安全:Stack是在方法上進行了同步訪問,LinkedList則沒有,若需要同步,可通過Collections的同步方法實現。

Vector與ArrayList區別:

1.線程安全:Vector是在方法上進行了同步訪問,ArrayList則沒有,若需要同步,可通過Collections的同步方法實現。

2.成員變量:Vector增加了一個capacityIncrement成員屬性,在擴容時該變量指定情況則直接增加相應步長容量。ArrayList則是增加約1.5倍容量。

 

經過三篇的篇幅,List接口的下的常用集合我們已經分析完啦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章