探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?

小橘子2222發表於2019-03-05

實驗

jdk版本1.8,測試平臺mbp2016

在資料尾部插入資料

測試程式碼

package com.lly.springtest1.collection;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * @ClassName ICollection
 * @Description TODO
 * @Author lly
 * @Date 2019/3/5 9:29 AM
 * @Version 1.0
 **/
@Slf4j
public class ICollection {

    public static void test() {
        LinkedList<String> links = new LinkedList<>();
        int len = 6553631;
        log.info(len + "");
        long btime = System.currentTimeMillis();
        for (int i = 0; i < len; i++) {
            links.add("sss");
        }
        long etime = System.currentTimeMillis();
        log.info("耗時:{},大小:{}", etime - btime, links.size());

        long btime1 = System.currentTimeMillis();
        ArrayList<String> arrays = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            arrays.add("sss");
        }
        long etime1 = System.currentTimeMillis();
        log.info("耗時:{},大小:{}", etime1 - btime1, arrays.size());

    }

    public static void main(String[] args) {
        ICollection.test();
    }
}

複製程式碼

測試結果

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?
此時我們的數量級別是百萬級別,我們驚訝的發現ArrayList插入效率要比LinkedList快接近20倍,為什麼?why?我們明明記得在學習java集合的時候,明確的知道是ArrayList查詢快,增刪慢的,LinkedList的特細則與之相反的,可是現實測試卻跟定義不一樣呢,那我們在多做點測試,改變數量級別看看十萬,萬,千級別的測試結果,我們改變插入集合的資料量大小測試。
首先降低到十萬級別

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?
發現測試的插入效率已經很接近了
萬級別

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?
這時候我們發現和十萬級別的測試結果差不多,還是ArrayList插入快一點
接著我們降低到千級別

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?
此時兩者的效率差不多,還是沒有體現出書中定義的2者關於插入效率的問題,問題還是沒有得到解決,那麼我們思考一下,上面的實驗我們都是預設插入的末尾的,是否跟我麼插入的順序有關係嗎,我們下面測試一下將資料插入的頭部

在陣列頭部插入資料

package com.lly.springtest1.collection;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * @ClassName ICollection
 * @Description TODO
 * @Author lly
 * @Date 2019/3/5 9:29 AM
 * @Version 1.0
 **/
@Slf4j
public class ICollection {

    public static void test() {
        LinkedList<String> links = new LinkedList<>();
        int len = 100000;
        log.info(len + "");
        long btime = System.currentTimeMillis();
        for (int i = 0; i < len; i++) {
            links.addFirst("sss");
        }
        long etime = System.currentTimeMillis();
        log.info("耗時:{},LinkedList大小:{}", etime - btime, links.size());

        long btime1 = System.currentTimeMillis();
        ArrayList<String> arrays = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            arrays.add(0,"sss");
        }
        long etime1 = System.currentTimeMillis();
        log.info("耗時:{},ArrayList大小:{}", etime1 - btime1, arrays.size());

    }

    public static void main(String[] args) {
        ICollection.test();
    }
}

複製程式碼

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?
此時我們發現,在萬級別LinkedList的插入效能就看出來了

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?
此時我們發現,在十萬級別LinkedList的插入效能是ArrayList的100+倍,那麼我們下面再測試一下在陣列中間部分插入資料

在陣列中間插入資料

package com.lly.springtest1.collection;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * @ClassName ICollection
 * @Description TODO
 * @Author lly
 * @Date 2019/3/5 9:29 AM
 * @Version 1.0
 **/
@Slf4j
public class ICollection {

    public static void test() {
        LinkedList<String> links = new LinkedList<>();
        int len = 10000;
        log.info(len + "");
        long btime = System.currentTimeMillis();
        for (int i = 0; i < len; i++) {
            links.add(links.size() / 2, "sss");
        }
        long etime = System.currentTimeMillis();
        log.info("耗時:{},LinkedList大小:{}", etime - btime, links.size());

        long btime1 = System.currentTimeMillis();
        ArrayList<String> arrays = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            arrays.add(arrays.size() / 2, "sss");
        }
        long etime1 = System.currentTimeMillis();
        log.info("耗時:{},ArrayList大小:{}", etime1 - btime1, arrays.size());

    }

    public static void main(String[] args) {
        ICollection.test();
    }
}

複製程式碼

資料量萬級別Linkedlist插入效率比ArrayList慢20倍

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?
資料量十萬級別Linkedlist插入效率比ArrayList慢160+倍
探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?

下面讓我係統測試一下其他數量級資料,並且加上隨機插入測試

public static void test(int listSize, int type) {
        LinkedList<String> links = new LinkedList<>();
        log.info("陣列長度:{},插入方式{}", listSize, type);
        long btime = System.currentTimeMillis();
        for (int i = 0; i < listSize; i++) {
            switch (type) {
                case 0:
                    links.addFirst("測試資料");
                    break;
                case 1:
                    links.add(links.size() / 2, "測試資料");
                    break;
                case 2:
                    links.addLast("測試資料");
                    break;
                default:
                    Random random = new Random();
                    int rd = random.nextInt(links.size()+1);
                    links.add(rd, "測試資料");
            }

        }
        long etime = System.currentTimeMillis();
        log.info("耗時:{}ms,LinkedList大小:{}", etime - btime, links.size());

        long btime1 = System.currentTimeMillis();
        ArrayList<String> arrays = new ArrayList<>();
        for (int i = 0; i < listSize; i++) {
            switch (type) {
                case 0:
                    arrays.add(0, "測試資料");
                    break;
                case 1:
                    arrays.add(arrays.size() / 2, "測試資料");
                    break;
                case 2:
                    arrays.add("測試資料");
                    break;
                default:
                    Random random = new Random();
                    int rd = random.nextInt(arrays.size()+1);
                    arrays.add(rd, "測試資料");
            }

        }
        long etime1 = System.currentTimeMillis();
        log.info("耗時:{}ms,ArrayList大小:{}", etime1 - btime1, arrays.size());

    }
複製程式碼

測試結果

  • 在集合頭部插入實驗資料

    探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?

  • 在集合中間插入實驗資料

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?

  • 在集合尾部插入實驗資料

    探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?

  • 在集合隨機位置插入資料

探索JAVA系列(二)LinkedList插入資料真的比ArrayList快嗎?

原始碼分析

在指定位置插入

LinkedList原始碼

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

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
複製程式碼
  • 判斷是否超過連結串列長度,超過則拋錯誤
  • 如果是插入最後的位置直接使用linkLast方法而不必去遍歷查詢對應位置
  • node方法尋找index所指向的Node,首先判斷index是否大於size/2,大於則從末尾往前找,小於則從0開始往後找
  • 找到之後就是new一個node物件,設定指標的問題
    LinkedList:效能主要在於遍歷連結串列查詢index

ArrayList原始碼

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

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
複製程式碼
  • rangeCheckForAdd判斷index是否合法
  • ensureCapacityInternal判斷是否需要擴容
  • arraycopy陣列複製,從index開始把後面的陣列元素全部複製到相對後一位的位置,該方法是native方法而且是連續記憶體複製問題,因此效能影響也沒想象中的大
  • elementData將element賦值給陣列index元素

ArrayList:影響ArrayList效能的主要因素是擴容和陣列複製,但是當size很大時陣列擴容影響就會變小,那麼此時的效率就會提升,此時如果在中間部分插入資料時候,我們要插入的位置為i,陣列長度是n,那麼就要變動i之後的n-i的資料。

在頭部部插入

LinkedList原始碼

public void addFirst(E e) {
    linkFirst(e);
}
private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}
複製程式碼

可以看到LinkedList直接在頭部時候不必遍歷,所以效率很高,體現了LinkedList的插入效率高的特性

ArrayList原始碼解釋同(指定位置插入)

在尾部插入

LinkedList原始碼

  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++;
    }
複製程式碼

LinkedList:用傳入的值new一個Node物件,然後尾指標指向該新的Node

ArrayList原始碼

  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
複製程式碼

ArrayList:如果超出容量需要擴容,不需擴容時直接陣列元素賦值

結論:當資料量越來越大時,ArrayList比LinkedList快
原因:當資料量大時,ArrayList每次擴容都能得到很大的新空間,解決了前期頻繁擴容的劣勢,而LinkedList雖然有尾指標,但是每次add都要將物件new成一個Node(而ArrayList直接陣列對應位置元素賦值)

結論

資料量\插入位置 頭部 中間 尾部 隨機
效率持平 效率持平 效率持平 效率持平
LinkedList插入快 效率持平 效率持平 ArrayList插入快
LinkedList插入快 ArrayList插入快 效率持平 ArrayList插入快
十萬 LinkedList插入快 ArrayList插入快 ArrayList插入快 ArrayList插入快
百萬 LinkedList插入快 ArrayList插入快 ArrayList插入快 ArrayList插入快
  • 在尾部插入資料,資料量較小時LinkedList比較快,因為ArrayList要頻繁擴容,當資料量大時ArrayList比較快,因為ArrayList擴容是當前容量*1.5,大容量擴容一次就能提供很多空間,當ArrayList不需擴容時效率明顯比LinkedList高,因為直接陣列元素賦值不需new Node
  • 在首部插入資料,LinkedList較快,因為LinkedList遍歷插入位置花費時間很小,而ArrayList需要將原陣列所有元素進行一次System.arraycopy
  • 插入位置越往中間,LinkedList效率越低,因為它遍歷獲取插入位置是從兩頭往中間搜,index越往中間遍歷越久,因此ArrayList的插入效率可能會比LinkedList高
  • 插入位置越往後,ArrayList效率越高,因為陣列需要複製後移的資料少了,那麼System.arraycopy就快了,因此在首部插入資料LinkedList效率比ArrayList高,尾部插入資料ArrayList效率比LinkedList高
  • inkedList可以實現佇列,棧等資料結構,這是它的優勢

相關文章