Java容器——Vector(Java9)原始碼解析
Vector是Java早期實現的容器,自JDK1.0就存在。目前在日常使用中逐漸被ArrayList或同步的ConcurrentLinkedQueue等代替,不過在早期的程式碼中和類庫中,仍經常能見到Vector,因此有必要對其的組成和原理進行基本的瞭解。
Vector的繼承關係如上圖所示,可以看到,Vector和ArrayList的繼承關係完全一致(可參考筆者先前的文章點選開啟連結)。那為何有了ArrayList還要構造出一個Vector呢?那下面從原始碼角度來了解一下Vector。
一 組成元素
/**
*
* 儲存元素的陣列
*
* @serial
*/
protected Object[] elementData;
/**
*
* 元素個數
*/
protected int elementCount;
/**
* 擴容數量
*
*/
protected int capacityIncrement;
這幾個變數從字面意思都比較容易理解,其中最後一個capacityIncrement是當vector容量不夠了,需要一次性進行擴容的數量。需要說明的是,同ArrayList一樣,capacity(也就是elementData.length)和elementCount是兩個值,一個顯示了當前vector能承載多少元素,一個是已經存放的元素。
二 關鍵函式
1 建構函式
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) { this(initialCapacity, 0); } public Vector() { this(10); } public Vector(Collection<? extends E> c) { elementData = c.toArray(); elementCount = elementData.length; // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, elementCount, Object[].class); }
建構函式都比較簡單明瞭,圍繞著initialCapacity和capacityIncrement展開,設定了初始化長度和每次增長的長度。
2 增刪改查
public synchronized void insertElementAt(E obj, int index) {
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
modCount++;
final int s = elementCount;
Object[] elementData = this.elementData;
if (s == elementData.length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = obj;
elementCount = s + 1;
}
插入元素前,首先判斷下標和擴容,然後將舊陣列拷貝到新建立的陣列中,實現過程比較清晰。需要注意的是,插入元素使用了synchronized關鍵字,即這個操作是加鎖的操作,而且是對整個vector操作加鎖,這就阻塞了對該vector的使用。
public synchronized void removeElementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
modCount++;
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
public synchronized E set(int index, E element) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
刪除操作是將指定刪除位置以後的元素依次前移一位,設定元素是將指定位置的元素用給定元素代替,也都是加鎖的操作。
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
如果說前面的增刪改操作涉及到對vector的修改,加鎖為了保證執行緒安全性可以理解。而獲取元素這個對vector來說並不改變其本身同時也是最體現List效能的方法上,加鎖就只能是拖累效能了。
小結
vector由於其功能和實現與ArrayList基本一致,不過對重要操作都使用了加鎖導致其效能有較大損失,在實際使用過程中使用並不太多。將其get功能去鎖的有CopyOnWriteArrayList,既不影響其執行緒安全性,又有效提升隨機訪問速度。