1. 程式人生 > >LinkedList和ArrayList異同

LinkedList和ArrayList異同

LinkedList定義:List介面的連結串列實現,並提供了一些佇列,棧,雙端佇列操作的方法;特點:1、分配記憶體空間不是必須是連續的;          2、插入、刪除操作很快,只要修改前後指標就OK了;          3、訪問比較慢,必須得從第一個元素開始遍歷;ArrayList定義:是一個動態陣列。特點:1、隨機訪問速度快,插入和移除效能較差(陣列的特點);          2、支援null元素;          3、有順序;          4、元素可以重複;          5、執行緒不安全;ArrayList和LinkedList的相同點:ArrayList和Linkedlist都實現List介面。ArrayList和LinkedList的不同點:以增加和刪除元素為例比較ArrayList和LinkedList的不同之處:(1)增加元素到列表尾端:在ArrayList中增加元素到佇列尾端的程式碼如下:
public boolean add(E e){
   ensureCapacity(size+1);//確保內部陣列有足夠的空間
   elementData[size++]=e;//將元素加入到陣列的末尾,完成新增
   return true;      
} 
ArrayList中add()方法的效能決定於ensureCapacity()方法。ensureCapacity()的實現如下:
public vod ensureCapacity(int minCapacity){
  modCount++;
  int oldCapacity=elementData.length;
  if(minCapacity>oldCapacity){    //如果陣列容量不足,進行擴容
      Object[] oldData=elementData;
      int newCapacity=(oldCapacity*3)/2+1;  //擴容到原始容量的1.5倍
      if(newCapacitty<minCapacity)   //如果新容量小於最小需要的容量,則使用最小
                                                    //需要的容量大小
         newCapacity=minCapacity ;  //進行擴容的陣列複製
         elementData=Arrays.copyof(elementData,newCapacity);
  }
}
LinkedList 的add()操作實現如下,它也將任意元素增加到佇列的尾端:
public boolean add(E e){
   addBefore(e,header);//將元素增加到header的前面
   return true;
}
其中addBefore()的方法實現如下:
private Entry<E> addBefore(E e,Entry<E> entry){
     Entry<E> newEntry = new Entry<E>(e,entry,entry.previous);
     newEntry.provious.next=newEntry;
     newEntry.next.previous=newEntry;
     size++;
     modCount++;
     return newEntry;
}
(2)增加元素到列表任意位置:由於ArrayList是基於陣列實現的,而陣列是一塊連續的記憶體空間,如果在陣列的任意位置插入元素,必然導致在該位置後的所有元素需要重新排列,因此,其效率相對會比較低。以下程式碼是ArrayList中的實現:
public void add(int index,E element){
   if(index>size||index<0)
      throw new IndexOutOfBoundsException(
        "Index:"+index+",size: "+size);
         ensureCapacity(size+1);
         System.arraycopy(elementData,index,elementData,index+1,size-index);
         elementData[index] = element;
         size++;
}
對LinkedList來說,在List的尾端插入資料與在任意位置插入資料是一樣的,不會因為插入的位置靠前而導致插入的方法效能降低。以下程式碼是LinkedList中的實現:
public void add(int index,E element){
   addBefore(element,(index==size?header:entry(index)));
}
(3)刪除任意位置元素:對ArrayList來說,在任意位置移除元素後,都要進行陣列的重組。ArrayList的實現如下:
public E remove(int index){
   RangeCheck(index);
   modCount++;
   E oldValue=(E) elementData[index];
  int numMoved=size-index-1;
  if(numMoved>0)
     System.arraycopy(elementData,index+1,elementData,index,numMoved);
     elementData[--size]=null;
     return oldValue;
}
在LinkedList的實現中,首先要通過迴圈找到要刪除的元素。如果要刪除的位置處於List的前半段,則從前往後找;
 若其位置處於後半段,則從後往前找。因此無論要刪除較為靠前或者靠後的元素都是非常高效的。LinkedList的實現如下:
public E remove(int index){
  return remove(entry(index));         
}
private Entry<E> entry(int index){
  if(index<0 || index>=size)
      throw new IndexOutBoundsException("Index:"+index+",size:"+size);
      Entry<E> e= header;
      if(index<(size>>1)){//要刪除的元素位於前半段
         for(int i=0;i<=index;i++)
             e=e.next;
     }else{
         for(int i=size;i>index;i--)
             e=e.previous;
     }
         return e;
}
(4)容量引數:它表示初始化的陣列大小。ArrayList提供了一個可以制定初始陣列大小的建構函式:
public ArrayList(int initialCapacity) 
(5)遍歷列表:構造一個擁有100萬資料的ArrayList和等價的LinkedList,用forEach操作,迭代器和for迴圈遍歷。測試結果的相對耗時如下表所示:可以看到,最簡便的ForEach迴圈並沒有很好的效能表現,綜合性能不如普通的迭代器,而是用for迴圈通過隨機訪問遍歷列表時,ArrayList表項很好,但是LinkedList的表現卻無法讓人接受,甚至沒有辦法等待程式的結束。這是因為對LinkedList進行隨機訪問時,總會進行一次列表的遍歷操作。效能非常差,應避免使用。