Vector和ArrayList對比

本文會對ArrayListVector進行分析,爲何會關注這兩個類,主要是由於他們擁有相同的繼承結構,接下來就來探索下這兩個類實現和效率的異同。java

繼承結構

能夠看到,VectorArrayList都實現了ListRandomAccess接口,都繼承了AbstractList。經過他們的繼承結構,大體能夠猜想他們在元素的處理上存在不少相同的地方。數組

存儲結構

VectorArrayList都使用 Object [] elementData保存數據,可是不一樣的ArrayListelementData使用transient作了標記,這說明ArrayListelementData不參與對象序列化的過程。安全

添加元素

  • 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)

在元素的添加上,VectorArrayList差很少提供了相同的接口,可是最大的不一樣是Vector提供的接口中,除了add(int, E)以外,都是同步接口,可是add(int, E)最終會調用同步方法insertElementAt(E obj, int ),故Vector添加元素都是同步方法;ArrayList添加元素的方法都是非同步方法。bash

訪問

  • Vector
    get(int index)
    elementAt(int index)
  • ArrayList
    get(int index)

在對元素的隨機訪問上,VectorArrayList多了一個elementAt(int index)函數,可是elementAt(int index)get(int index)原理是同樣的,故能夠總結爲VectorArrayList在隨機訪問元素時實現了一樣的接口。最大的不一樣仍然是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方式遍歷最大的區別是他們不是同步的,主要緣由是以上兩種遍歷方法不會在遍歷過程當中對集合中的數據進行修改。併發

擴容

因爲使用數組存儲元素,在元素不斷的增長程中,VectorArrayList都須要對數組容量進行增長,在數組容量變化上,VectorArrayList選擇了不同的策略。dom

  • 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,不然容量直接增長一倍。ide

  • 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的時候,大多數場景是單線程的環境,本文就在單線程的環境中對VectorArrayList進行對比,這種對比不是精確的對比,只是對比一下快慢。本文從添加,遍歷和隨機訪問三個方面進行對比。測量的方法比較簡單,就是先向集合中添加元素,而後再去遍歷元素,最後分別統計添加元素,遍歷元素和隨機訪問的耗時,測試的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時,VectorArrayList在元素的添加,遍歷和隨機訪問上已經沒有什麼性能差別或者說差別很小。

總結

ArrayListVector都是java中比較重要的容器,他們均可以存儲各類對象,它們有相同的繼承結構,提供大體相同的功能,主要的差別點以下:

  • Vector是線程安全的容易,能夠在併發環境中安全地使用,而ArrayList是非線程安全的
  • ArrayList進行擴容時增長50%,Vector提供了擴容時的增量設置,但一般將容量擴大1倍
  • Vector可使用Enumeration和Iterator進行元素遍歷,ArrayList只提供了Iterator的方式
  • 因爲使用的線程同步,Vector的效率比ArrayList

自java1.6以後,爲了優化synchronized,java引入了偏向鎖,在單線程環境中,Vector的效率已經被提升了。從剛纔的對比也能夠發現,在單線程環境中,數據量較少(測試數據量在100000性能差別較小)的狀況下,VectorArrayList的性能差別已經不明顯了。

關注微信公衆號,最新技術乾貨實時推送

image
相關文章
相關標籤/搜索