JDK源碼閱讀之Vector

Vector

      Vector與ArrayList十分相似,只是ArrayList【讀我】是線程不安全的,而Vector的實現是線程安全的。現在一起來看看它的實現吧!

類圖

Vector類圖

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

      可以發現Vector繼承的類和實現的接口與ArrayList一模一樣。

類前註釋

      由iterator()listIterator(int)方法返回的迭代器是fail-fast的:如果列表在迭代器創建之後的任何時間被結構化地修改,除了通過迭代器自己的remove或add方法之外,迭代器將會拋出ConcurrentModificationException。 因此,面對併發修改,迭代器將快速而乾淨地失敗,而不是在未來未確定的時間冒着任意的非確定性行爲。由elements()返回的Enumerations 不是fail-fast的。

      Vector是同步的。 如果不需要線程安全的實現,建議使用ArrayList代替Vector。

成員變量

/*
 * 顧名思義,此變量用於存儲Vector的數據,同時我們也知道了Vector底層也是數組實現的。
 * Vector的容量是此數組緩衝區的長度,並且至少足夠大以包含所有向量的元素。
 * 注:Vector中最後一個元素後面的任何數組元素都爲空。
 */
protected Object[] elementData;

/*
 * 該Vector對象中有效組件的數量。 組件elementData[0]至elementData[elementCount-1]是實際項目。
 */
protected int elementCount;

/*
 * 當Vector的大小大於其容量時,Vector的容量自動增加的量。
 * 如果容量增量小於或等於零,則每次需要增長時,向量的容量將加倍。
 */
protected int capacityIncrement;

構造方法

public Vector() {
	this(10);
}

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

      通過構造方法我們知道如果使用無參構造方法默認初始化容量大小爲10,且每次擴容時向量的容量將加倍(因爲capacityIncrement<=0)。指定的初始化容量必須大於等於0,否則拋異常。

public Vector(Collection<? extends E> c) {
	elementData = c.toArray();
	elementCount = elementData.length;
	// c.toArray也許不會返回Object[]類型
	if (elementData.getClass() != Object[].class)
		elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

      按照集合的迭代器返回的順序,構造一個包含指定集合元素的向量。

copyInto

public synchronized void copyInto(Object[] anArray) {
	System.arraycopy(elementData, 0, anArray, 0, elementCount);
}

      將此Vector中的組件複製到指定的數組中。Vector中索引k處的項目被複制到anArray的索引k處。可以發現此方法使用synchronized修飾了,所以這是線程安全的,我們可以發現其實Vector中大量方法大部分是使用它完成同步的,所以Vector的效率其實是不高的。

trimToSize

public synchronized void trimToSize() {
	modCount++;
	int oldCapacity = elementData.length;
	if (elementCount < oldCapacity) {
		elementData = Arrays.copyOf(elementData, elementCount);
	}
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
	@SuppressWarnings("unchecked")
	T[] copy = ((Object)newType == (Object)Object[].class)
		? (T[]) new Object[newLength]
		: (T[]) Array.newInstance(newType.getComponentType(), newLength);
	System.arraycopy(original, 0, copy, 0,
					 Math.min(original.length, newLength));
	return copy;
}

      修改該Vector的容量爲Vector的當前大小。底層通過新建一個大小爲Vector大小的數組並將原數據拷貝過去來實現,所以elementData變化了。

grow

      此方法用於Vector的擴容。在與檢查容量相關方法中都需要使用此方法。

private void grow(int minCapacity) {
	//minCapacity表示所需的最小容量
	int oldCapacity = elementData.length;
	//通過此行代碼可以知道如果capacityIncrement<=0,則雙倍擴容。
	int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
	//如果capacityIncrement擴容後的新容量依舊小於minCapacity,則新容量就是minCapacity
	if (newCapacity - minCapacity < 0)
		newCapacity = minCapacity;
	//擴容後的新容量大於MAX_ARRAY_SIZE,我們需要重新計算容量
	//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	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;
}

ensureCapacity

      確認Vector的容量是否滿足要求(minCapacity表示所需的最小容量),過小則使用grow方法進行擴容。

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); //擴容
}

setSize

      設置Vector的大小。如果新的大小大於當前大小,則新的項目將添加到Vector的末尾。 如果新的大小小於當前尺寸,則將newSize後的所有元素置爲null。

public synchronized void setSize(int newSize) {
	modCount++;
	if (newSize > elementCount) {
		//新大小大於當前大小,需要確認一下容量,判斷是否需要擴容
		ensureCapacityHelper(newSize);
	} else {
		for (int i = newSize ; i < elementCount ; i++) {
			elementData[i] = null;//將newSize後的所有元素置爲null
		}
	}
	elementCount = newSize;
}

capacity

      返回當前Vector的容量。

public synchronized int capacity() {
    return elementData.length;
}

size

      返回當前Vector包含項目的數目。

public synchronized int size() {
    return elementCount;
}

isEmpty

      判斷當前Vector是否爲空,爲空返回true,否則返回false。

public synchronized boolean isEmpty() {
    return elementCount == 0;
}

elements

      返回Vector中元素的枚舉。返回的Enumeration對象將生成Vector中的所有項。產生的第一項索引爲0,第二項索引爲1,依此類推。在遍歷過程中我們可以之間進行添加和刪除操作,說明其不提供fail-fast機制

indexOf

      返回Vector中指定元素的第一次出現位置的索引,如果此向量不包含元素,則返回-1。與此方法類似的還有lastIndexOf,只是此方法返回最後一次出現位置的索引。

public int indexOf(Object o) {
	return indexOf(o, 0);
}
public synchronized int indexOf(Object o, int index) {
	if (o == null) {
		for (int i = index ; i < elementCount ; i++)
			if (elementData[i]==null)
				return i;
	} else {
		for (int i = index ; i < elementCount ; i++)
			if (o.equals(elementData[i]))
				//返回第一個符合條件的索引
				return i;
	}
	//找不到返回-1
	return -1;
}

elementAt

      返回指定索引的元素。與其類似的方法有firstElementlastElement,返回第一個元素和最後一個元素,還有get(int index)

setElementAt

      設置指定索引的元素。與之類似的有set(int index, E element)

removeElementAt

      刪除指定索引的元素。removeElement(Object obj)方法調用此方法實現刪除操作(先找位置再刪除),remove(Object o)方法調用removeElement實現。

public synchronized void removeElementAt(int index) {
	modCount++;
	if (index >= elementCount) {
		throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
	}
	else if (index < 0) {
		throw new ArrayIndexOutOfBoundsException(index);
	}
	//計算index後面的元素個數
	int j = elementCount - index - 1;
	if (j > 0) {
		//將index後面的元素複製到前一位
		System.arraycopy(elementData, index + 1, elementData, index, j);
	}
	elementCount--;
	elementData[elementCount] = null; /* to let gc do its work */
}

remove

      刪除指定索引位置的元素,返回舊值。

removeIf

      傳入一個謂詞,滿足指定條件的元素。

public static void main(String[] args) {
	Vector<Integer> vector = new Vector<Integer> (100);
	vector.add(1);vector.add(2);
	vector.add(3);vector.add(4);
	vector.add(5);vector.add(6);
	vector.add(7);vector.add(8);
	boolean b = vector.removeIf(n -> n % 2 == 0);
	Iterator<Integer> iterator = vector.iterator();
	while (iterator.hasNext()){
		System.out.print(iterator.next() + " ");
	}
}
//1 3 5 7

removeAllElements

      刪除Vector所有元素。底層將Vector中的所有元素置爲null,一切就交給GC了。clear方法使用此方法完成。

insertElementAt

      往指定索引中插入元素。Vector的add(int index, E element)方法調用此方法實現。

public synchronized void insertElementAt(E obj, int index) {
	modCount++;
	if (index > elementCount) {
		throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
	}
	//插入元素前需要確認還有容量
	ensureCapacityHelper(elementCount + 1);
	//將index後(包括index)的元素後移一位
	System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
	//設置index索引上的值爲新值
	elementData[index] = obj;
	elementCount++;
}

addElement

      往Vector尾部添加元素,與之類似的方法是add(E e)方法,但是add方法有返回值(返回true)。

public synchronized void addElement(E obj) {
	modCount++;
	//添加前先確認容量
	ensureCapacityHelper(elementCount + 1);
	//直接設置最後一個位置爲新對象
	elementData[elementCount++] = obj;
}

addAll

      不僅僅可以一個個添加元素,還可以直接添加整個集合呢。

public synchronized boolean addAll(Collection<? extends E> c) {
	modCount++;
	Object[] a = c.toArray();
	int numNew = a.length;
	//不多說,看看有位置沒
	ensureCapacityHelper(elementCount + numNew);
	//將a中的元素全部拷貝到elementData中
	System.arraycopy(a, 0, elementData, elementCount, numNew);
	//更新elementCount
	elementCount += numNew;
	//如果numNew爲0則返回false
	return numNew != 0;
}

迭代器

      Vector爲我們提供了兩種迭代器,分別爲ListItr和Itr。使用ListItr迭代器我們可以完成遍歷,添加和修改節點的操作,使用ListItr我們不僅可以正向遍歷鏈表也可以反向遍歷。
      使用Itr我們只能正向遍歷,並且只提供刪除操作,無法添加和修改節點。

文章同步【個人站】

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