Java小白集合源碼的學習系列:Vector


Vector源碼學習

前文傳送門:
Java小白集合源碼的學習系列:LinkedList
Java小白集合源碼的學習系列:ArrayList
Vector是JDK1.0中的集合,是集合中的老大哥,其中大部分的方法都被synchronized關鍵字所修飾,與ArrayList和LinkedList不同,它是線程安全的(關於線程安全,之后學習再做系統總結)。但是隨着一系列的更新迭代,它的缺點漸漸暴露:如方法名字太長,實現接口時出現了許多重復多余的方法等等。
從JDK1.2開始,Vector類被改進以實現List接口,讓它成為Java集合框架的一員,如果不需要線程安全,建議使用ArrayList,效率更高。

Vector繼承體系

還是按照慣例,先看看它的繼承圖,當然這張圖是基於JDK1.8的。
1SwsaV.png

  • 可以看出來它和ArrayList一樣,繼承了AbstractList,實現List接口。
  • 實現了RandomAccess接口,支持隨機訪問。
  • 實現了Cloneable接口,實現了克隆的功能。
  • 實現了Serializable接口,支持序列化。

Vector核心源碼

基本屬性

    //存儲元素的數組
    protected Object[] elementData;
    //元素個數
    protected int elementCount;

    //該值決定了增長機制的不同
    protected int capacityIncrement;
    private static final long serialVersionUID = -2767605614048989439L;

構造器

我們可以得出結論:Vector的底層也是基於數組實現的,但是這些屬性和我們之前提到的ArrayList有什么不同之處呢?我們繼續向下看它所提供的幾個構造器:

    //兩個參數,創建initialCapacity大小的數組,並為capacityIncrement賦值
    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(int initialCapacity, int capacityIncrement)
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

    //無參構造器,調用Vector(int initialCapacity)
    public Vector() {
        this(10);
    }

    //傳入集合
    public Vector(Collection<? extends E> c) {
        elementData = c.toArray();
        elementCount = elementData.length;
        //類型判斷
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }

我們可以發現:

  • initialCapacity代表的是數組的容量,我們可以指定容量,不指定默認為10
  • capacityIncrement從字面上看,就可以知道它代表的是容量增量,意味着這個值將會影響之后的擴容,可以指定,不指定默認為0。

擴容機制

那么我們繼續來看看它的擴容機制,是否可以驗證我們的說法:
基本上的部分,都是和ArrayList類似,我們直接截取有差異的部分:

    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        //如果增量大於0,新容量 = 原容量 + 增量
        //如果增量不大於0,新容量 = 原容量*2
        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);
    }

其他的部分就不做分析了,之前講的很詳細,可以看看前面的文章。我們需要關注的是:

  • 如果指定增量(增量>0),那么每次在擴容的時候,新容量就是在原容量的基礎上加上指定增量
  • 如果沒有指定增量,那么每次在擴容的時候,新容量默認變成原容量的兩倍

Enumeration

概述

說起迭代器,我們總是第一個想到的就是Iterator,而再Iterator是在JDK1.2的時候誕生的,用於取代JDK1.0版本的唯一迭代器Enumeration。官方對它的解釋是這樣的:

An object that implements the Enumeration interface generates a series of elements, one at a time. Successive calls to the nextElement method return successive elements of the series.

我用拙劣的英語試着翻譯一下:實現Enumeration這個接口的對象呢,將會生成一系列的元素,生成的時候是一個一個生成的,通過調用nextElement這個方法,就可以返回這個系列里所有的連續元素。

今天我們勵志做個光榮的官方文檔搬運工!

Methods are provided to enumerate through the elements of a vector, the keys of a hashtable, and the values in a hashtable. Enumerations are also used to specify the input streams to a SequenceInputStream.

這段的意思也很明白:Enumeration接口為Vector的元素hashtable的鍵和值提供了枚舉的方法,它也被運用到指定SequenceInputStream的輸入流中。我們暫時只需要知道,Vector類中,有一種方法能夠產生Eumeration對象就完事了。其他的我們后面會進行總結。

接下來這段話相當關鍵!官方文檔中用了大寫的NOTE:

NOTE:The functionality of this interface is duplicated by the Iterator interface. In addition, Iterator adds an optional remove operation, and has shorter method names. New implementations should consider using Iterator in preference to Enumeration.

大致的意思就是:現在這個方法呢,不太適應潮流了,那個年代用起來挺不錯,現在需要年輕一輩來代替了。這個新一代的產物就是Iterator,它復制了Enumeration的功能,並且增加可選的remove操作,而且提供了更簡短的命名。官方仿佛在嬉皮笑臉對你說:親,這邊建議你迭代器盡量用Iterator喲。

但是盡管如此,我們還需需要了解以下它的基本操作,畢竟以后可能還是會見到。

源碼描述

//Enumeration接口  
public interface Enumeration<E> {
    //判斷是否還有更多的元素
    boolean hasMoreElements();
    //沒有下一個元素就報錯,有就返回
    E nextElement();
}
//Vector中的elements方法對接口的實現
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");
        }
    };
}

具體操作

    Vector<String> v = new Vector<>();
    v.add("天");
    v.add("喬");
    v.add("巴");
    v.add("夏");
    //利用vector對象產生迭代器對象
    Enumeration<String> e = v.elements();
    //判斷后邊是否還有元素
    while(e.hasMoreElements()){
        //挪動指針指向下一個元素
        System.out.print(e.nextElement()+" ");
    }
    //天 喬 巴 夏 

Vector總結

  • 底層基於數組,可以實現動態擴增,支持根據索引快速訪問。
  • 如果沒有指定容量,默認為10。如果沒有指定增量,默認為0。
  • 擴容時,如果增量>0,則新容量=指定增量+原容量;如果增量<=0,則新容量為原容量的兩倍。(注意,Vector里也有實現確定容量的方法ensureCapacity)
  • 線程安全,如在創建迭代器之后的任何時間對結構進行修改,除了迭代器本身的remove和add外,都會拋出ConcurrentModification異常。所以,Vector通過synchronized關鍵字實現線程同步。可以參考:https://blog.csdn.net/yjclsx/article/details/85283169

本文若有敘述不當之處,還望各位評論區加以批評指正,謝謝。

參考資料:JDK1.8官方文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM