ArrayList與linkedlist插入效率分析
網上有很多比較ArrayList和LinkedList的文章,基本上都認為ArrayList是用陣列實現,而LinkedList用連結串列實現,所以ArrayList查詢效率高,LinkedList插入效率高,但是事實真的是這樣嗎?
比較他們的使用效率之前,先看看ArrayList與LinkeLlist的底層資料結構。
- /**
- * The array buffer into which the elements of the ArrayList are stored.
- * The capacity of the ArrayList is the length of this array buffer.
- */
- private transient Object[] elementData;
- /**
- * The size of the ArrayList (the number of elements it contains).
- *
- * @serial
- */
- private int size;
ArrayList內部用陣列來儲存元素,size記錄當前陣列的大小。但是陣列的大小是確定的,那麼他是怎麼實現ArrayList這種大小可變的集合的呢?
- public boolean add(E e) {
- ensureCapacity(size + 1); // Increments modCount!!
- elementData[size++] = e;
- return true;
- }
- /**
- * Increases the capacity of this <tt>ArrayList</tt> instance, if
- * necessary, to ensure that it can hold at least the number of elements
- * specified by the minimum capacity argument.
- *
- * @param minCapacity the desired minimum capacity
- */
- public void ensureCapacity(int minCapacity) {
- modCount++;
- int oldCapacity = elementData.length;
- if (minCapacity > oldCapacity) {
- Object oldData[] = elementData;
- int newCapacity = (oldCapacity * 3)/2 + 1;
- if (newCapacity < minCapacity)
- newCapacity = minCapacity;
- // minCapacity is usually close to size, so this is a win:
- elementData = Arrays.copyOf(elementData, newCapacity);
- }
- }
- 每次ArrayList在新增元素的時候都會呼叫ensureCapacity方法,這個方法會檢查陣列容量是否夠大,如果不夠則新建立一個容量為1.5倍的陣列,
- 並把資料複製到這個新陣列上。
- 所以不能簡答的說ArrayList底層資料結構是一個陣列,其實它是一個動態表。
接下來看看LinkedList的實現,它是一個雙向迴圈連結串列。
- //連結串列大小,transient代表不可序列化
- transient int size = 0;
- /**
- * 指向頭節點的指標
- */
- transient Node<E> first;
- /**
- * 指向尾節點的指標
- */
- transient Node<E> last;
- private static class Node<E> {
- E item;
- Node<E> next; // 指向後一個結點
- Node<E> prev; // 指向前一個結點
- Node(Node<E> prev, E element, Node<E> next) {
- this.item = element;
- this.next = next;
- this.prev = prev;
- }
- }
- 值得注意的是,這裡的連結串列節點是linkedlist的私有內部類,也就是說其他類是不可能拿到節點的引用的。所以在連結串列中間插入的時候,通過修改幾個指標就能插入一個節點這樣的操作是不可能的。
瞭解底層實現之後,我們開始比較ArrayList與linkedlist的插入效率。
1.尾部插入
- public boolean add(E e) {
- ensureCapacity(size + 1); // Increments modCount!!
- elementData[size++] = e;
- return true;
-
}
ArrayList最常用的add方法就是尾部插入,在不需要擴容的情況下,只需要將size遞增即可,時間複雜度O(1) - 需要擴容的情況下,時間複雜度也不會受到影響。理由如下:
- 假設它每次擴容兩倍,那麼從開始的16個元素擴容到n,共需要複製16+32+。。。+n/4+n/2+n<2n個元素
- 所以平均到每一次新增操作,擴容只需要複製常數個元素
- /**
- *預設的新增動作,可以看到這個方法是把新元素新增 到表尾
- */
- public boolean add(E e) {
- addBefore(e, header); //加到頭結點之前 ,即表尾
- return true;
- }
- /**
- *將元素e新增到entry結點之前
- */
- private Entry<E> addBefore(E e, Entry<E> entry) {
- Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
- newEntry.previous.next = newEntry; //將新結點與前後結點相連線
- newEntry.next.previous = newEntry;
- size++;
- modCount++;
- return newEntry;
- }
- 可以看出linkedlist的最常用的add方法也是插入尾部,只需要改變幾個指標,時間複雜度O(1)
綜上,在兩個集合最常用的尾部插入時間複雜度都是O(1),不存在ArrayList插入比linkedlist慢的說法
2.中間插入
- public void add(int index, E element) {
- if (index > size || index < 0)
- throw new IndexOutOfBoundsException(
- "Index: "+index+", Size: "+size);
- ensureCapacity(size+1); // Increments modCount!!
- System.arraycopy(elementData, index, elementData, index + 1,
- size - index);
- elementData[index] = element;
- size++;
- }
- ArrayList的中間插入效率比較低,因為需要將index之後的元素往後移動,時間複雜度O(n),但是不代表ArrayList就比linkedlist慢
-
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
} -
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
} - linkedlist雖然插入的時間複雜度為O(1),但是在每次插入前需要找到插入的位置。前面已經說過linkedlist的node節點是私有內部類,外部不可能拿到node的引用,所以linkedlist只能進行順序定址找到插入的位置,總時間複雜度也是O(n)
綜上,ArrayList與linkedlist在中間插入的時間複雜度也是一樣
3.迭代器插入
linkedlist和ArrayList內部都內建了一個迭代器用來遍歷他們裡面的元素,在遍歷的時候也可以插入元素。ArrayList的迭代器插入用的是中間插入的方法,所以時間複雜度還是一樣。但是linkedlist在迭代過程中不需要定位插入的位置,插入的時間複雜度變成O(1)。所以在需要迭代器插入O(n)個元素的時候,ArrayList時間複雜度為O(n^2),而linkedlist的時間複雜度為O(n),這時候才體現了連結串列插入的優勢。但是如果只需要插入常數個元素的時候,迭代器遍歷的開銷大於插入的開銷,此時兩個集合的時間複雜度還是一樣。
所以,不能簡單的說ArrayList插入速度比linkedlist慢,附上別人做的效能測試,僅供參考
http://blog.csdn.net/dlutbrucezhang/article/details/9931025
相關文章
- Java資料結構之LinkedList、ArrayList的效率分析Java資料結構
- Java ArrayList 與 LinkedListJava
- ARRAYLIST VECTOR LINKEDLIST 區別與用法
- 探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?Java
- Java ArrayList 與 LinkedList 的靈活選擇Java
- List集合總結,對比分析ArrayList,Vector,LinkedList
- Java-ArrayList & LinkedList的原始碼對比分析Java原始碼
- 資料結構:用例項分析ArrayList與LinkedList的讀寫效能資料結構
- ArrayList & LinkedList原始碼解析原始碼
- ArrayList和LinkedList的區別?
- ArrayList和LinkedList的區別
- ArrayList和LinkedList區別 javaJava
- ArrayList和LinkedList的比較
- ArrayList和LinkedList如何實現的?
- Java List 常用集合 ArrayList、LinkedList、VectorJava
- Java 集合 ArrayList VS LinkedList VS VectorJava
- Java中ArrayList和LinkedList區別Java
- LinkedList和ArrayList的區別、Vector和ArrayList的區別
- LinkedList與Queue原始碼分析原始碼
- java中的List介面(ArrayList、Vector、LinkedList)Java
- ArrayList和LinkedList的區別是什麼
- ArrayList,LinkedList,Vector,Stack之間的區別
- [原始碼分析]ArrayList和LinkedList如何實現的?我看你還有機會!原始碼
- ArrayList和LinkedList的幾種迴圈遍歷方式及效能對比分析
- LinkedList分析
- java集合【12】——— ArrayList,LinkedList,Vector的相同點與區別是什麼?Java
- ArrayList分析
- List集合(ArrayList-LinkedList);Set集合(HashSet-TreeSet)
- java複習之 Vector、ArrayList和LinkedList 的區別Java
- 說出 ArrayList,Vector, LinkedList 的儲存效能和特性?
- arraylist陣列元素插入及顯示示例陣列
- LinkedList原始碼分析原始碼
- ArrayList,HashMap,LinkedList 初始化大小和 擴容機制HashMap
- ArrayList、Vector、LinkedList的區別及其優缺點? (轉載)
- Java 容器和泛型(2)ArrayList 、LinkedList和Vector比較Java泛型
- ArrayList 原始碼分析原始碼
- [原始碼分析]ArrayList原始碼
- ArrayList原始碼分析原始碼