Vector和ArrayList對比
本文會對ArrayList
和Vector
進行分析,為什麼會關注這兩個類,主要是因為他們擁有相同的繼承結構,接下來就來探索下這兩個類實現和效率的異同。
繼承結構
可以看到,Vector
和ArrayList
都實現了List
和RandomAccess
介面,都繼承了AbstractList
。通過他們的繼承結構,大致可以猜測他們在元素的處理上存在很多相同的地方。
儲存結構
Vector
和ArrayList
都使用 Object [] elementData
儲存資料,但是不同的ArrayList
的elementData
使用transient
做了標記,這說明ArrayList
的elementData
新增元素
- Vector
add(E)
add(int, E)
addElement(E obj)
addAll(Collection<? extends E> c)
addAll(int, Collection<? extends E> c)
insertElementAt(E obj, int )
- ArrayList
add(E)
add(int, E)
addAll(Collection<? extends E> c)
addAll(int, Collection<? extends E> c)
在元素的新增上,Vector
ArrayList
差不多提供了相同的介面,但是最大的不同是Vector
提供的介面中,除了add(int, E)
之外,都是同步介面,但是add(int, E)
最終會呼叫同步方法insertElementAt(E obj, int )
,故Vector
新增元素都是同步方法;ArrayList
新增元素的方法都是非同步方法。
訪問
- Vector
get(int index)
elementAt(int index)
- ArrayList
get(int index)
在對元素的隨機訪問上,Vector
比ArrayList
多了一個elementAt(int index)
函式,但是elementAt(int index)
get(int index)
原理是一樣的,故可以總結為Vector
和ArrayList
在隨機訪問元素時實現了同樣的介面。最大的不同仍然是Vector
對元素的隨機訪問是同步的,而ArrayList
是非同步的。
遍歷
ArrayList
提供了foreach
, Iterator
的遍歷方式,Vector
除此之外還提供了另外兩種遍歷方式:
Vector<String> sVector = new Vector<>();
for (int i = 0 ; i < 5 ; i++) {
sVector.add("test" + i);
}
sVector.forEach(new Consumer<String>() {
@Override
public void accept(String t) {
// TODO Auto-generated method stub
System.out.println(t);
}
});
Enumeration<String> enumeration = sVector.elements();
while (enumeration.hasMoreElements()) {
System.out.println(enumeration.nextElement());
}
複製程式碼
在Vector
中,這兩種方式和使用Iterator
方式遍歷最大的區別是他們不是同步的,主要原因是以上兩種遍歷方法不會在遍歷過程中對集合中的資料進行修改。
擴容
由於使用陣列儲存元素,在元素不斷的增加程中,Vector
和ArrayList
都需要對陣列容量進行增加,在陣列容量變化上,Vector
和ArrayList
選擇了不一樣的策略。
- Vector
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
.......
}
複製程式碼
在擴容的時候,如果capacityIncrement > 0
(caoaciryIncrement
是新建Vector
時傳遞的第二個引數,但是在具體使用很少使用這個引數,故大多數情況下capacityIncrement=0
),則將容量增加capacityIncrement
,否則容量直接增加一倍。
- ArrayList
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
......
}
複製程式碼
ArrayList
的擴容很簡單,直接在原來容量的基礎上增加了50%。
效率究竟差多少
Vector
是執行緒安全的容器,它的很多方法都使用synchronzied方法進行了修飾,說明要使用Vector例項,需要先獲得鎖,這一過程比較耗時,但是究竟能耗時多少,是不是比ArrayList
耗時很多?本文不打算去測試在多執行緒環境下兩者的對比,因為在使用ArrayList
的時候,大多數場景是單執行緒的環境,本文就在單執行緒的環境中對Vector
和ArrayList
進行對比,這種對比不是精確的對比,只是對比一下快慢。本文從新增,遍歷和隨機訪問三個方面進行對比。測量的方法比較簡單,就是先向集合中新增元素,然後再去遍歷元素,最後分別統計新增元素,遍歷元素和隨機訪問的耗時,測試的java環境是jdk1.8.0_181。
- Vector
long start = System.currentTimeMillis();
for (int i = 0 ; i < 500000 ; i++) {
sVector.add("qiwoo_test_add" + i);
}
long end = System.currentTimeMillis();
System.out.println("vector add time consuming:" + (end - start));
Iterator<String> iterator = sVector.iterator();
long visitStart = System.currentTimeMillis();
while (iterator.hasNext()) {
String str = iterator.next();
}
long visitEnd = System.currentTimeMillis();
System.out.println("vector visit time consuming:" + (visitEnd -visitStart));
long randAccessStart = System.currentTimeMillis();
for (int i = 0 ; i < 500000 ; i++) {
sVector.get(i);
}
long randAccessend = System.currentTimeMillis();
System.out.println("vector random access time consuming:" +(randAccessend - randAccessStart));
複製程式碼
在我的電腦上,執行的結果如下:
vector add time consuming:95
vector visit time consuming:18
vector random access time consuming:14
- ArrayList
long start = System.currentTimeMillis();
for (int i = 0 ; i < 500000 ; i++) {
sArray.add("qiwoo_test_add" + i);
}
long end = System.currentTimeMillis();
System.out.println("array add time consuming:" + (end - start));
Iterator<String> iterator = sArray.iterator();
long visitStart = System.currentTimeMillis();
while (iterator.hasNext()) {
String str = iterator.next();
}
long visitEnd = System.currentTimeMillis();
System.out.println("array visit time consuming:" + (visitEnd -visitStart));
long randAccessStart = System.currentTimeMillis();
for (int i = 0 ; i < 500000 ; i++) {
sArray.get(i);
}
long randAccessend = System.currentTimeMillis();
System.out.println("array random access time consuming:" +(randAccessend - randAccessStart));
複製程式碼
在我的電腦上執行結果如下:
array add time consuming:82
array visit time consuming:11
array random access time consuming:5
上面的結果可以發現,在單執行緒環境下,在元素新增和遍歷上,Vector
均比ArrayList
慢了一些,其中新增元素慢了8%左右,遍歷元素慢了64%,隨機訪問慢了1.8倍,這些資料可能受資料量的不同而不同,但是整體的趨勢應該是一致的。
以上測試的時候,資料量為500000,但是實際進行Android開發的過程中產生的資料量比較少,參考下google設計容器時的數量考慮,接下來把資料量設定為1000,看下執行結果的差異
- Vector
vector add time consuming:2
vector visit time consuming:1
array random access time consuming:0 - ArrayList
array add time consuming:2
array visit time consuming:1
array random access time consuming:0
當資料量到1000時,Vector
和ArrayList
在元素的新增,遍歷和隨機訪問上已經沒有什麼效能差異或者說差異很小。
總結
ArrayList
和Vector
都是java中比較重要的容器,他們都可以儲存各種物件,它們有相同的繼承結構,提供大致相同的功能,主要的差異點如下:
Vector
是執行緒安全的容易,可以在併發環境中安全地使用,而ArrayList
是非執行緒安全的ArrayList
進行擴容時增加50%,Vector
提供了擴容時的增量設定,但通常將容量擴大1倍Vector
可以使用Enumeration和Iterator進行元素遍歷,ArrayList
只提供了Iterator的方式- 由於使用的執行緒同步,
Vector
的效率比ArrayList
低
自java1.6之後,為了優化synchronized,java引入了偏向鎖,在單執行緒環境中,Vector
的效率已經被提高了。從剛才的對比也可以發現,在單執行緒環境中,資料量較少(測試資料量在100000效能差異較小)的情況下,Vector
和ArrayList
的效能差異已經不明顯了。