Vector源碼解析

簡介

Vector 看上去想一個可增加的數組,可使用索引訪問。他的size隨着添加或刪除item可大可小。Vector爲了優化存儲,保存着capacity和capacityIncrement兩個變量。capacity最小爲vector當前的大小。通常比vector的size要大,當有有數據添加到vector時,vector的大小以capacityIncrement的整數倍增長。java

vector的迭代器也是fail-fast的,若是在迭代器建立後vector的結構被修改了(除了調用迭代器自己的remove或者add方法),將會拋出異常。數組

since v1.2 vector 實現了List接口,並且開始支持多線程。支持多線程的方式是在方法前加上synchronized修飾符,實現對象鎖,這種鎖的效率很低。安全

構造函數

Vector的構造函數有以下四個:多線程

// 能夠指定初始容量,和每次擴容的數量
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                                initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
    // 調用第一個構造函數,每次擴容數量爲0 其內部在擴容時
    // 判斷了若是capacityIncrement<= 0 時,直接擴容到原來
    // 容量的兩倍,在grow函數中。
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    // 默認構造函數,初始容量爲10,jdk1.8中ArrayList默認構造函數的初始容量爲0在第一調用add時將其初始化爲10
    public Vector() {
        this(10);
    }
    // 支持使用集合類建立
    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內部使用數組存儲:函數

protected Object[] elementData;

當數組大小不夠用時,它會使用內部的擴容機制擴容。擴容機制寫的很嚴謹,考慮到了int超過最大值的狀況。優化

// 在添加元素會調用傳進來的minCapacity 爲當前vector內部的數據個數,加上要增長的數據個數 minCapacity 由於使用的int,在擴容時有可能超過int最大值而變成一個負數
    public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            ensureCapacityHelper(minCapacity);
        }
    }
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        // 若是須要的容量大於當前數組的容量才擴容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }   
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 若是構造函數中沒有設置capacityIncrement,則擴容到以前容量的兩倍
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        // 若是擴容到原來的兩倍還不夠用,則使用指定的容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
            // 一樣考慮超過容許的最大值狀況,將調用hugeCapactity
            // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
            // 虛擬機爲數組保留了一些頭信息
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    // 若是超過Integer.MAX_VALUE-8 則返回int最大值,不然返回Integer.MAX_VALUE-8
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

總結一下,在擴容時,若是定義了capacityIncrement,按照capacityIncrement增長,若是capacityIncrement爲0,將容量擴大到原來的兩倍,擴容後若是還不夠用的話,按照實際大小擴容。其中若是擴容後的大小超過了int的最大值,將報錯。this

枚舉器 Enumeration

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

從代碼中能夠看出其結構很相似interator的內部實現,調用hasMoreElement判斷是否還有元素,調用nextElement迭代出下一個元素。可是其內部沒有像迭代器中的remove方法,很純粹,是一個只讀的模型。他與迭代器最大的區別是,當枚舉建立後,若是對vector的結構作了修改,他不會拋出ConcurrentModificationException。可是若是在代碼中像以下的方式使用,仍是會出問題。code

public void enumNoFailFastTest() {
        Vector<String> vector = new Vector<>();
        vector.add("chris");
        vector.add("mumu");
        Enumeration<String> enumeration = vector.elements();

        System.out.println(enumeration.nextElement());
        if (enumeration.hasMoreElements()) {
            vector.remove(1);
            System.out.println(enumeration.nextElement());
        }
    }

上述代碼運行後拋出異常:java.util.NoSuchElementException: Vector Enumeration。緣由是當用hashasMoreElements後,將第二個元素移除了,這時再次調用nextElement時,發現count=1 == elementCount了。這時就會拋出異常。這裏是模擬在一個線程在判斷還有元素(最後一個元素)後,第二個線程移除了這個元素,第一個線程再訪問這個元素就會出錯。基於上面的分析,若是在寫代碼時用到了elements這個函數,須要本身處理線程安全的問題。對象

其餘知識點

除此以外,vector還提供了一些相似數組按照索引訪問的功能。例如,indexOf,lastIndexOf,firstElement,lastElement,setElement,removeElement,elementAt等函數,這裏再也不講其內部實現。其內部使用了不少一下兩個關於數組的函數,對日常coding頗有幫助。

//數組的copy函數
    // src : 從哪一個數組copy數據
    // srcPos : 從數組的那個索引開始copy
    // dest : copy 到哪一個函數中,
    // destPos : copy的數據從dest的第幾個索引開始存儲
    // length: copy數據的長度
    System.arraycopy(Object src,  int  srcPos, Object dest, int destPos,int length);
    // 以給定的數據類型,指定的數組長度建立一個數組
    Array.newInstance(newType.getComponentType(), newLength);

Vector支持排序,在排序的過程當中,vector若是發生結構變化,也會拋出異常。而排序內部使用Arrays提供的排序方法。

public synchronized void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, elementCount, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

疑問

在vector的源碼註解中有一個shrink,意思當刪除數據時,他會自動縮小。可是在代碼中沒有看到關於縮小elementData的操做,只有一個trimTosize()函數。若是非要說他能夠縮小所佔空間的話,應該指的是這個函數。

相關文章
相關標籤/搜索