最近在看Vector與ArrayList的源碼,看下他們的區別與聯系。
- Vector是線程安全的集合類,ArrayList並不是線程安全的類。Vector類對集合的元素操作時都加了synchronized,保證線程安全。
- Vector與ArrayList本質上都是一個Object[] 數組,ArrayList提供了size屬性,Vector提供了elementCount屬性,他們的作用是記錄集合內有效元素的個數。與我們平常調用的arrayList.size()和vector.size()一樣返回的集合內有效元素的個數。
- Vector與ArrayList的擴容並不一樣,Vector默認擴容是增長一倍的容量,Arraylist是增長50%的容量。
- Vector與ArrayList的remove,add(index,obj)方法都會導致內部數組進行數據拷貝的操作,這樣在大數據量時,可能會影響效率。
- Vector與ArrayList的add(obj)方法,如果新增的有效元素個數超過數組本身的長度,都會導致數組進行擴容。
先看下他們的源碼是怎么定義內部數據存儲的:
1 private static final long serialVersionUID = 8683452581122892189L; 2 3 /** 4 * Default initial capacity. 5 */ 6 private static final int DEFAULT_CAPACITY = 10; 7 8 /** 9 * Shared empty array instance used for empty instances. 10 */ 11 private static final Object[] EMPTY_ELEMENTDATA = {}; 12 13 /** 14 * The array buffer into which the elements of the ArrayList are stored. 15 * The capacity of the ArrayList is the length of this array buffer. Any 16 * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to 17 * DEFAULT_CAPACITY when the first element is added. 18 */ 19 private transient Object[] elementData; 20 21 /** 22 * The size of the ArrayList (the number of elements it contains). 23 * 24 * @serial 25 */ 26 private int size;
這是ArrayList的定義,他首先定義了他的初始化容量為10
private static final int DEFAULT_CAPACITY = 10
這里應該看到了數據存儲是放在Object數組里的
private transient Object[] elementData
定義了數據的長度size
The size of the ArrayList (the number of elements it contains)
再看看Vector的定義:
1 /** 2 * The array buffer into which the components of the vector are 3 * stored. The capacity of the vector is the length of this array buffer, 4 * and is at least large enough to contain all the vector's elements. 5 * 6 * <p>Any array elements following the last element in the Vector are null. 7 * 8 * @serial 9 */ 10 protected Object[] elementData; 11 12 /** 13 * The number of valid components in this {@code Vector} object. 14 * Components {@code elementData[0]} through 15 * {@code elementData[elementCount-1]} are the actual items. 16 * 17 * @serial 18 */ 19 protected int elementCount; 20 21 /** 22 * The amount by which the capacity of the vector is automatically 23 * incremented when its size becomes greater than its capacity. If 24 * the capacity increment is less than or equal to zero, the capacity 25 * of the vector is doubled each time it needs to grow. 26 * 27 * @serial 28 */ 29 protected int capacityIncrement;
Vector定義了數組
protected Object[] elementData;
有效元素個數
protected int elementCount
Vector增長容量,默認0
protected int capacityIncrement
Vector和ArrayList在元素超過初始大小時擴容是不一樣的,但是也不像網上說的Vector增長是按一倍增長,我覺得應該加默認兩個字才對,Vector中的元素個數超過了初始化容量的話,默認確實會增長一倍,請看代碼:
1 /** 2 * The maximum size of array to allocate. 3 * Some VMs reserve some header words in an array. 4 * Attempts to allocate larger arrays may result in 5 * OutOfMemoryError: Requested array size exceeds VM limit 6 */ 7 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 8 9 private void grow(int minCapacity) { 10 // overflow-conscious code 11 int oldCapacity = elementData.length; 12 int newCapacity = oldCapacity + ((capacityIncrement > 0) ? 13 capacityIncrement : oldCapacity); 14 if (newCapacity - minCapacity < 0) 15 newCapacity = minCapacity; 16 if (newCapacity - MAX_ARRAY_SIZE > 0) 17 newCapacity = hugeCapacity(minCapacity); 18 elementData = Arrays.copyOf(elementData, newCapacity); 19 }
代碼里在判斷增長容量(簡稱增量)參數時如果增量大於0時,是會按增量進行擴容的,否則的話才會增加一倍的容量到數組中。
而Vector在初始化加載構造函數時,開發人員是可以指定其增量的大小的,並不是必須要根據增加一倍的規則進行增加。還是看代碼:
1 public Vector(int initialCapacity, int capacityIncrement) { 2 super(); 3 if (initialCapacity < 0) 4 throw new IllegalArgumentException("Illegal Capacity: "+ 5 initialCapacity); 6 this.elementData = new Object[initialCapacity]; 7 this.capacityIncrement = capacityIncrement; 8 } 9 10 /** 11 * Constructs an empty vector with the specified initial capacity and 12 * with its capacity increment equal to zero. 13 * 14 * @param initialCapacity the initial capacity of the vector 15 * @throws IllegalArgumentException if the specified initial capacity 16 * is negative 17 */ 18 public Vector(int initialCapacity) { 19 this(initialCapacity, 0); 20 } 21 22 /** 23 * Constructs an empty vector so that its internal data array 24 * has size {@code 10} and its standard capacity increment is 25 * zero. 26 */ 27 public Vector() { 28 this(10); 29 }
可以看到在構造函數中已經表明,可以指定其增量的大小,如果沒有指定默認0。數組的初始化大小為10。
但是ArrayList就不可以進行增量的修改指定。還是看代碼:
1 /** 2 * Increases the capacity to ensure that it can hold at least the 3 * number of elements specified by the minimum capacity argument. 4 * 5 * @param minCapacity the desired minimum capacity 6 */ 7 private void grow(int minCapacity) { 8 // overflow-conscious code 9 int oldCapacity = elementData.length; 10 int newCapacity = oldCapacity + (oldCapacity >> 1); //看這里 11 if (newCapacity - minCapacity < 0) 12 newCapacity = minCapacity; 13 if (newCapacity - MAX_ARRAY_SIZE > 0) 14 newCapacity = hugeCapacity(minCapacity); 15 // minCapacity is usually close to size, so this is a win: 16 elementData = Arrays.copyOf(elementData, newCapacity); 17 }
首先在構造函數中,ArrayList就沒有提供相應的設置增量的方法,而擴容方法grow中直接就對數組進行增量50%的操作了,並沒有相應的參數設置或判斷增量的大小。
在上邊都提到了一個靜態常量是private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
這個常量是數組擴容的最大長度,但是為什么-8呢我看了上邊的描述,感覺應該是:
Some VMs reserve some header words in an array.
有些虛擬機會在數組頭部加入一些信息,如果還是設置最大的話,可能會導致OOM
這就是Vector和ArrayList在擴容及成員變量方面的區別及聯系了。
接下來我們看看他們的源碼中插入、刪除等方法為什么說和LinkedList相比要慢
先看代碼:
1 public synchronized void insertElementAt(E obj, int index) { 2 modCount++; 3 if (index > elementCount) { 4 throw new ArrayIndexOutOfBoundsException(index 5 + " > " + elementCount); 6 } 7 ensureCapacityHelper(elementCount + 1); 8 System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); 9 elementData[index] = obj; 10 elementCount++; 11 }
Vector集合在add(int index, E element)時實際上調用了insertElementAt方法,他和刪除方法實際上都是對數組進行了copy,所以在大數據量時可能會導致效率降低。ArrayList也是這樣的情況。
但是在增加調用add(E e)方法時,其實就是在數組中追加數據了,如果追加數據的長度大於實際數組長度的話,會進入到grow擴容方法進行擴容。
Vector和ArrayList都提供了trimToSize()方法,這個方法是對數組容量進行縮減的方法。在這個方法中,調用方法時會對數組的元素容量及數組本身長度進行判斷,如果數組內實際元素的個數比數組本身的長度少的話,調用這個方法會將數組縮減到元素個數大小。在數據量大的時候可以考慮這樣做,這樣可以節省不必要的空間浪費。看代碼:
1 public void trimToSize() { 2 modCount++; 3 if (size < elementData.length) { 4 elementData = Arrays.copyOf(elementData, size); 5 } 6 }
Vector本身提供了一個同步方法叫setSize方法,該方法可以對當前集合進行長度設置。如果設置的長度比當前元素個數要大的話會進行判斷是否需要擴容,如果不是,在給定的Size值外的元素將被置為空值。
看代碼:
1 public synchronized void setSize(int newSize) { 2 modCount++; 3 if (newSize > elementCount) { 4 ensureCapacityHelper(newSize); 5 } else { 6 for (int i = newSize ; i < elementCount ; i++) { 7 elementData[i] = null; 8 } 9 } 10 elementCount = newSize; 11 }
這大體上就是Vector和ArrayList的區別
ps:以上只是個人理解,如有不對請指正。