Vector與ArrayList 的理解


  最近在看Vector與ArrayList的源碼,看下他們的區別與聯系。

  1. Vector是線程安全的集合類,ArrayList並不是線程安全的類。Vector類對集合的元素操作時都加了synchronized,保證線程安全。
  2. Vector與ArrayList本質上都是一個Object[] 數組,ArrayList提供了size屬性,Vector提供了elementCount屬性,他們的作用是記錄集合內有效元素的個數。與我們平常調用的arrayList.size()和vector.size()一樣返回的集合內有效元素的個數。
  3. Vector與ArrayList的擴容並不一樣,Vector默認擴容是增長一倍的容量,Arraylist是增長50%的容量。
  4. Vector與ArrayList的remove,add(index,obj)方法都會導致內部數組進行數據拷貝的操作,這樣在大數據量時,可能會影響效率。
  5. 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:以上只是個人理解,如有不對請指正。

 


免責聲明!

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



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