1. 程式人生 > >Java集合類原始碼解析:Vector

Java集合類原始碼解析:Vector

引言

之前的文章我們學習了一個集合類 ArrayList,今天講它的一個兄弟 Vector
為什麼說是它兄弟呢?因為從容器的構造來說,Vector 簡直就是 ArrayList 的翻版,也是基於陣列的資料結構,不同的是,Vector的每個方法都加了 synchronized 修飾符,是執行緒安全的。

類宣告

用idea開啟 Vector 的原始碼,不難發現,它的類宣告跟 ArrayList 一模一樣,都是繼承了AbstractList,並且都實現了RandomAccess 介面,遍歷元素用for迴圈的效率要優於迭代器。

 * @author  Lee Boynton
 * @author  Jonathan Payne
 * @see Collection
 * @see LinkedList
 * @since   JDK1.0
 */
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

值得說明的是,從註釋上來看,Vector 是 JDK1.0版本就引進的,屬於最古老的集合類的那一批,而ArrayList是 1.2才引進的,所以說,Vector才是哥哥,ArrayList是小弟,哈哈~~~~

基本變數和建構函式

基本變數

Vector 的基本變數有四個,分別是:

  • 底層陣列
protected Object[] elementData;
  • 陣列元素個數
protected int elementCount;
  • 增長的容量大小,如果這個值小於或等於0,擴容時會擴大 2 倍,
capacityIncrement
  • 最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

建構函式

//建立初識容量為10的陣列,增長量0
public Vector() {
    this(10);
}
//建立初識容量可變的陣列,增長量為0
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}
//建立初識容量可變的陣列,可設定增長量
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                        initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}
//建立一個包含指定集合的陣列
public Vector(Collection<? extends E> c) {
    elementData = c.toArray();
    elementCount = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
       elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
 }

看的出來,Vector的構造器和成員變數和ArrayList大同小異。

成員方法

擴容

Vector 與 ArrayList 雖然很類似,但在擴容大小這方面還是有區別的,ArrayList 預設擴容後的大小為原容量 的1.5倍,而Vector則是先判斷增長量大小,如果是非正數,那就擴大為原來的2倍,看一下它的擴容方法:

//引數是最小需要的容量
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //如果增長量不大於0,擴容為2倍大小
    //一般預設建立的容器都是不傳增長量的,所以預設增長量是0,也就是預設直接擴容兩倍
    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);
}
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

新增

Vector的新增方法都是加上 synchronized關鍵字的,並且新增前檢測容量,判斷是否擴容:

//加入元素到陣列結尾,同步的
public synchronized boolean add(E e) {
    modCount++;
    //檢測容量
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
} 
//檢測容量大小,超過陣列長度就做擴容
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
public void add(int index, E element) {
    insertElementAt(element, index);
}
//插入對應索引的元素
public synchronized void insertElementAt(E obj, int index) {
        modCount++;
        if (index > elementCount) {
            throw new ArrayIndexOutOfBoundsException(index
                                                     + " > " + elementCount);
        }
        ensureCapacityHelper(elementCount + 1);
        //插入元素前,把其索引後面的元素統一後移一位
        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
        elementData[index] = obj;
        elementCount++;
    }
public synchronized void addElement(E obj) {
    modCount++;
    //保證容量足夠
    ensureCapacityHelper(elementCount + 1);
    //直接設定最後一個元素的資料
    elementData[elementCount++] = obj;
}
//新增整個集合
public synchronized boolean addAll(Collection<? extends E> c) {
        modCount++;
        //把集合轉為陣列物件
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityHelper(elementCount + numNew);
        //直接複製集合元素到陣列後面
        System.arraycopy(a, 0, elementData, elementCount, numNew);
        elementCount += numNew;
        return numNew != 0;
    }
 //在對應的索引處插入一個集合
public synchronized boolean addAll(int index, Collection<? extends E> c) {
        modCount++;
        if (index < 0 || index > elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityHelper(elementCount + numNew);
        //計算要移動多少個元素
        int numMoved = elementCount - index;
        if (numMoved > 0)
            //把插入位置後面的元素後移這麼多位
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
        //複製元素陣列
        System.arraycopy(a, 0, elementData, index, numNew);
        elementCount += numNew;
        return numNew != 0;
    }

Vector的新增方法程式碼不是很複雜,跟ArrayList 一樣,本質上都是對陣列做插入資料的操作,不同的是,方法都加了synchronized 修飾,所以,它的新增方法都是執行緒安全的。

其他操作元素的方法也是這樣的套路,這裡不打算一一列舉了,因為都跟ArrayList 差不多,另外,Vector 比 ArrayList 多了一個迭代方法

public Enumeration<E> elements() {
    return new Enumeration<E>() {
        int count = 0;

        public boolean hasMoreElements() {
            return count < elementCount;
        }

        public E nextElement() {
            synchronized (Vector.this) {
                if (count < elementCount) {
                    return elementData(count++);
                }
            }
            throw new NoSuchElementException("Vector Enumeration");
        }
    };
}

返回的是一個Enumeration 介面物件,大概也是個容器介面,沒用過,不說太多。

Vector 對比 ArrayList

最後,總結一下 Vector 和 ArrayList 的對比吧。

相同點:

  • 底層都是基於陣列的結構,預設容量都是10;

  • 都實現了RandomAccess 介面,支援隨機訪問;

  • 都有擴容機制;

區別:

  • Vector 的方法有做同步操作,是屬於執行緒安全的,而ArrayList 是非執行緒安全的;

  • Vector預設情況下擴容後的大小為原來的2倍,而ArrayList 是1.5倍;

  • Vector 比 ArrayList 多了一種迭代器 Enumeration;

雖然Vector相較ArrayList做了同步的處理,但這樣也影響了效率,因為每次呼叫方法都要獲取鎖,所以,一般情況下,對集合的執行緒安全沒有需求的話,推薦使用 ArrayList。