java集合的實現細節--ArrayList和LinkedList

來源:互聯網
上載者:User

標籤:過程   數組元素   尋找   element   調用   程式   修改   seq   node   

  ArrayList和LinkedList的實現差異 

  List代表一種線性表的資料結構,ArrayList則是一種順序儲存的線性表,ArrayList底層採用動態數組的形式儲存每一個集合元素,LinkedList則是一種鏈式儲存的線性表,其本質上就是一個雙向鏈表,它不僅實現了List介面,還實現了Deque介面,Deque代表了一種雙端隊列,既具有隊列(FIFO)的特性,也具有棧(FILO)的特性,也就是說,LinkedList既可以當成雙向鏈表使用,也可以當成隊列使用,還可以當成棧來使用。

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable

  ArrayList底層採用一個elementData數組來儲存所有集合元素,因此ArrayList在插入元素時需要完成兩件事情:

  ①、儲存ArrayList底層封裝的數組長度大於集合元素的個數

  ②、將插入位置之後的所有數組元素“整體搬家”,向後移動一“格”

  當刪除ArrayList集合中指定位置的元素時,程式也要進行“整體搬家”,而且還需要將被刪除索引處的數組元素賦值為null,下面是ArrayList集合的remove(int index)方法的原始碼。

public E remove(int index) {        rangeCheck(index);        modCount++;        E oldValue = elementData(index);        int numMoved = size - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,                             numMoved);        elementData[--size] = null; // clear to let GC do its work        return oldValue;    }

  對於ArrayList集合而言,當程式向ArrayList中添加、刪除集合元素時,ArrayList底層都是需要對數組進行“整體搬家”,因此效能很差。

  但如果程式調用get(int index)方法取出ArrayList集合中的元素時,效能和數組幾乎相同,效率非常快。下面是ArrayList集合的get(int index)方法的原始碼。

  E elementData(int index) {       return (E) elementData[index];  }       public E get(int index) {        rangeCheck(index);        return elementData(index);    }    

  LinkedList本質上是一個雙向鏈表,因此它使用內部類來儲存每一個集合元素。

 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; } }

  從上面程式中的粗體字代碼可以看出,一個Node對象代表雙向鏈表的一個節點,該對象中的next變數指向下一個節點,previous子指向上一個節點。

  由於LinkedList採用雙向鏈表來儲存集合元素,因此它在添加集合元素時,只要對鏈表進行插入即可。下面是LinkedList添加節點的源碼。

     public void add(int index, E element) {        checkPositionIndex(index);        if (index == size)            linkLast(element);        else            linkBefore(element, node(index));    }

  從上面代碼看出,由於Linked本質上就是一個雙向鏈表,因此它非常方便的在指定節點之前插入新節點。

  上面add(int index,E element)方法實現中用到了以下三個方法。

  ①、node(int index):搜尋指定索引出的元素

  ②、linkLast(E e):將e新節點插入到最後

  ③、linkBefore(E e,Node<E> succ):在succ節點之前插入e新節點

  node(int index)實際上就是get(int index)方法的底層實現。Linked必須逐個元素的搜尋,直到找到第index個元素為止。node(int index)採用二分法的方式進行尋找,以下是該方法的原始碼。

   Node<E> node(int index) {        // assert isElementIndex(index);     //如果index小於size/2        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;        }    }

  上面的node(int index)方法就是逐個元素的找到index索引處的元素,只是由於LinkedList是一個雙向鏈表,因此程式先根據index的值判斷它離鏈表頭近還是離鏈表尾近,如果離鏈表頭近則從頭端開始搜尋,如果離鏈表尾近則從尾端開始搜素。

  Linked的get(int index)方法只是對上面node(int index)方法的簡單封裝。get(int index)方法的原始碼如下。

    public E get(int index) {        checkElementIndex(index);        return node(index).item;    }

  但單純的插入操作就比較簡單了,只要修改幾個節點裡面的previous、next引用的值即可。下面是linkedbefore(E e, Node<E> succ)方法的原始碼。

   void linkBefore(E e, Node<E> succ) {        // assert succ != null;        final Node<E> pred = succ.prev;        final Node<E> newNode = new Node<>(pred, e, succ);        succ.prev = newNode;        if (pred == null)            first = newNode;        else            pred.next = newNode;        size++;        modCount++;    }

  如果只是單純的添加某個節點,那麼LinkedList的效能是非常好的,但如果需要向某個指定索引處添加節點,LinkedList必須先找到指定索引處的節點,這個搜尋過程的系統開銷並不小,因此LinkedList的add(int index,E e)方法的效能並不是特別好。如果只使用LinkedList的addFrist(E e)、addLast(E e)、offerFrist(E e)、offerLast(E e)、pollFrist(E e)、pollLast(E e)的話,效能是非常好的,因為可以避免搜尋過程。

  類似的,LinkedList為了實現remove(int index)方法,也必須先通過node(int index)方法找到index索引處的節點,然後修改它前一個節點的next引用,以及後一個節點的previous引用,下面是該方法的原始碼。

    public E remove(int index) {        checkElementIndex(index);        return unlink(node(index));    }    E unlink(Node<E> x) {        // assert x != null;        final E element = x.item;        final Node<E> next = x.next;        final Node<E> prev = x.prev;        if (prev == null) {            first = next;        } else {            prev.next = next;            x.prev = null;        }        if (next == null) {            last = prev;        } else {            next.prev = prev;            x.next = null;        }        x.item = null;        size--;        modCount++;        return element;    }

   綜上所述,ArrayList、LinkedList有各自適用的情境,大部分情況下,ArrayList的效能總是優於Linkedlist,因此絕大部分都應該考慮使用ArrayList集合。但如果程式荊楚需要添加、刪除元素,尤其是經常需要add(E e)方法向集合中添加元素,則應該考慮使用LinkedList集合了。

 

java集合的實現細節--ArrayList和LinkedList

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.