ArrayList和LinkedList的區別,包括源碼分析

其實也不像大家說的那回事,主要還是根據數據量和操作有關係!
我們先從add() 方法比較
ArrayList

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  
 //指定下標添加元素
  public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        /*
         * 判斷是否擴容數組
         */
        if (minCapacity - elementData.length > 0)
            //擴容數組
            grow(minCapacity);
    }

 中間省略了一些方法
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

 當ArrayList去add數據的時候
 elementData[size++] = e; 先去把元素添加進去 ,因爲容量肯定是夠用的

oldCapacity + (oldCapacity >> 1) 這裏面則是用到了位運算進行擴容數組

 elementData = Arrays.copyOf(elementData, newCapacity);

LikedList

public boolean add(E e) {
        linkLast(e);
        return true;
    }


 void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

  public void add(int index, E element) {
        checkPositionIndex(index);

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

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

對於add(int index,Element element)這個方法在中間添加數據
因爲ArrayList去添加數據 會移動數組或者擴容數組 !內部實現還是根據邏輯去擴容數組,是同數組具有下標的。

而LikedList在添加數據的時候只是new 一個Note對象並重新修改上一節點或下一個節點。達到目的

在內存方面ArrayList應該是佔的塊內存

我這面做的一個測試,如果add(0,element)的時候

getTime = System.currentTimeMillis();
        for(int i = 0; i < 900;i++){
            arrayList.add(0,i);
        }
        Log.e("time", "array" + (System.currentTimeMillis() - getTime));

   getTime = System.currentTimeMillis();
        for(int i = 0; i < 900;i++){
            linkedList.add(0,i);
        }
        Log.e("time", "linked" + (System.currentTimeMillis() - getTime));

小數據的時候偶爾集合會較快,偶爾鏈表快,當數據量大的時候會更加明顯集合大於鏈表的添加時間。

我這面做的一個測試,如果add(size,element)的時候
小數據的時候偶爾集合會較快,大多數還是鏈表快,當數據量大的時候會更加明顯集合大於鏈表的添加時間。

我這面做的一個測試,如果add(2,element)的時候 ,也就是Index的時候 小數據的時候偶爾集合會較快,大多數還是鏈表快,大數據的話還是鏈表快的

remove對比

ArrayList

public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(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; // clear to let GC do its work

        return oldValue;
    }

 通過下標index 執行這段代碼
 System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
 這面我舉個例子說明下
 因爲原碼裏面都是elementData,也就是一個數組,
 {0,1,2,3,4}
 如果當remove的index爲1的時候
 走完System.arraycopy()這個方法數組將會發生改變
 {0,2,3,4,4}

 然後 elementData[--size] = null; 
 size減減 保證arrayList.size()方法的準確拿取ArrayList的size大小
 並把數組的最後一個數據設爲null
 {0,2,3,4,null}
 可見移除了下標爲1的數據
 作爲
 public static void arraycopy(
                Object src,  //源數組
        int srcPos,  //源數組的起始位置
        Object dest, //目標數組
        int destPos, //目標數組的起始位置
        int length   //複製長度
                             )
     這個方法 當remove的時候內部實現會複製數組  index下標越小  複製的數組量越大,數組移動也就越多 耗時越大!

這面是這麼想的 但是當ArrayList add(int index)集合的時候,卻是index越小時間越短 和上面的分析完全相反!
這面我有點蒙,希望看到文章的評論解釋下~

LikedList

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
        checkElementIndex(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;
        }

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

先解釋第一個方法,  Node<E> node(int index)通過此方法其實也就是和二分法差不多 通過遍歷集合拿到此下標下的Note對象

然後在通過 E unlink(Node<E> x) 這一方法 重新關聯 prev 和 next 的Note對象 並把當前的Note對象
和當前Note對象的prev和next設置 null

所以相對於remove這一方法的總結 LikedList是相對快於ArrayList的

而對於get(int index)這個方法 可想而知肯定是ArrayList比較快的,因爲內部實現是數組是有下標的,而 LikedList 是通過和二分法差不多的查找Note對象。

最後總結下 :
add(0,element)
add(2,element)
add(size,element) ,
小數據確實ArrayList和 LikedList難以分辯誰快,因爲不是確定的
但是當數據大的話還是LikedList快於ArrayList的

remove(int index),LikedList快於ArrayList

get(int index),ArrayList快於LikedList

但是對於 public static void arraycopy(
Object src, //源數組
int srcPos, //源數組的起始位置
Object dest, //目標數組
int destPos, //目標數組的起始位置
int length //複製長度
)
這個方法還是存在困惑,對於ArrayList的add(index ,element)到底是index越大越費時還是index越小費時!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章