面試題——ArrayList和LinkedList的區別


List概括

先回顧一下List在Collection的框架圖:

從圖中可以看出:

  1. List是一個接口,他繼承Collection接口,代表有序的隊列。
  2. AbstractList是一個抽象類, ,它繼承與AbstractCollection。AbstractList實現了List接口中除了size()、get(int location)之外的方法。
  3. AbstractSequentialList是一個抽象類,它繼承與AbstrctList。AbstractSequentialList實現了“鏈表中,根據index索引值操作鏈表的全部方法”
  4. ArrayList、LinkedList、Vector和Stack是List的四個實現類,其中Vector是基於JDK1.0,雖然實現了同步,但是效率低,已經不用了,Stack繼承與Vector,所以不再贅述。
  5. LinkList是個雙向鏈表,它同樣可以被當做棧,隊列,或者雙端隊列。

ArrayList 和 LinkList區別

  1. ArrayList是實現了基於動態數組的數據結構,而LinkedList是基於鏈表的數據結構,。

  2. 對於隨機訪問的get和set,ArrayList要優於LinkedList,因為LinkedList基於指針的移動。

  3. 對於添加和刪除操作add和remove,一般大家都會說LinkedList要比ArrayList快,因為ArrayList要移動數據。但是實際情況並非這樣,對於添加或刪除,LinkedList和ArrayList並不能明確說明誰快誰慢,下面會詳細分析。

     我們結合之前分析的源碼,來看看為什么是這樣的:
    
     ArrayList中的隨機訪問、添加和刪除部分源碼如下:
    

*** //獲取index位置的元素值
public E get(int index) {
rangeCheck(index); //首先判斷index的范圍是否合法

return elementData(index);  

}

//將index位置的值設為element,並返回原來的值
public E set(int index, E element) {
rangeCheck(index);

E oldValue = elementData(index);  
elementData[index] = element;  
return oldValue;  

}

//將element添加到ArrayList的指定位置
public void add(int index, E element) {
rangeCheckForAdd(index);

ensureCapacityInternal(size + 1);  // Increments modCount!!  
//將index以及index之后的數據復制到index+1的位置往后,即從index開始向后挪了一位  
System.arraycopy(elementData, index, elementData, index + 1,  
                 size - index);   
elementData[index] = element; //然后在index處插入element  
size++;  

}

//刪除ArrayList指定位置的元素
public E remove(int index) {
rangeCheck(index);

modCount++;  
E oldValue = elementData(index);  

int numMoved = size - index - 1;  
if (numMoved > 0)  
    //向左挪一位,index位置原來的數據已經被覆蓋了  
    System.arraycopy(elementData, index+1, elementData, index,  
                     numMoved);  
//多出來的最后一位刪掉  
elementData[--size] = null; // clear to let GC do its work  

return oldValue;  

}


LinkedList中的隨機訪問、添加和刪除部分源碼如下:

//獲得第index個節點的值
*** public E get(int index) {
checkElementIndex(index);
return node(index).item;
}

//設置第index元素的值
public E set(int index, E element) {
checkElementIndex(index);
Node x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}

//在index個節點之前添加新的節點
public void add(int index, E element) {
checkPositionIndex(index);

if (index == size)  
    linkLast(element);  
else  
    linkBefore(element, node(index));  

}

//刪除第index個節點
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}

//定位index處的節點
Node node(int index) {
// assert isElementIndex(index);
//index<size/2時,從頭開始找
if (index < (size >> 1)) {
Node x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { //index>=size/2時,從尾開始找
Node x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
} ***

從源碼可以看出,ArrayList想要get(int index)元素時,直接返回index位置上的元素,而LinkedList需要通過for循環進行查找,雖然LinkedList已經在查找方法上做了優化,比如index < size / 2,則從左邊開始查找,反之從右邊開始查找,但是還是比ArrayList要慢。這點是毋庸置疑的。
ArrayList想要在指定位置插入或刪除元素時,主要耗時的是System.arraycopy動作,會移動index后面所有的元素;LinkedList主耗時的是要先通過for循環找到index,然后直接插入或刪除。這就導致了兩者並非一定誰快誰慢,下面通過一個測試程序來測試一下兩者插入的速度:

*** import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/*

  • @description 測試ArrayList和LinkedList插入的效率

  • @eson_15
    */
    public class ArrayOrLinked {
    static List array=new ArrayList ();
    static List linked=new LinkedList ();

    public static void main(String[] args) {

     //首先分別給兩者插入10000條數據  
     for(int i=0;i<10000;i++){    
         array.add(i);    
         linked.add(i);    
     }    
     //獲得兩者隨機訪問的時間  
     System.out.println("array time:"+getTime(array));    
     System.out.println("linked time:"+getTime(linked));    
     //獲得兩者插入數據的時間  
     System.out.println("array insert time:"+insertTime(array));    
     System.out.println("linked insert time:"+insertTime(linked));    
    

    }
    public static long getTime(List list){
    long time=System.currentTimeMillis();
    for(int i = 0; i < 10000; i++){
    int index = Collections.binarySearch(list, list.get(i));
    if(index != i){
    System.out.println("ERROR!");
    }
    }
    return System.currentTimeMillis()-time;
    }

    //插入數據
    public static long insertTime(List list){
    /*
    * 插入的數據量和插入的位置是決定兩者性能的主要方面,
    * 我們可以通過修改這兩個數據,來測試兩者的性能
    */
    long num = 10000; //表示要插入的數據量
    int index = 1000; //表示從哪個位置插入
    long time=System.currentTimeMillis();
    for(int i = 1; i < num; i++){
    list.add(index, i);
    }
    return System.currentTimeMillis()-time;

    }

}


****
index = 1000的時候
array time:7
linked time:567
array insert time:29
linked insert time:37

index = 5000的時候
array time:5
linked time:415
array insert time:15
linked insert time:128

index = 9000的時候
array time:6
linked time:443
array insert time:12
linked insert time:152

從運行結果看,LinkedList的效率是越來越差。

    所以當插入的數據量很小時,兩者區別不太大,當插入的數據量大時,大約在容量的1/10之前,LinkedList會優於ArrayList,在其后就劣與ArrayList,且越靠近后面越差。所以個人覺得,一般首選用ArrayList,由於LinkedList可以實現棧、隊列以及雙端隊列等數據結構,所以當特定需要時候,使用LinkedList,當然咯,數據量小的時候,兩者差不多,視具體情況去選擇使用;當數據量大的時候,如果只需要在靠前的部分插入或刪除數據,那也可以選用LinkedList,反之選擇ArrayList反而效率更高。


免責聲明!

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



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