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

實驗

jdk版本1.8,測試平臺mbp2016java

在數據尾部插入數據

測試代碼node

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

複製代碼

測試結果spring

此時咱們的數量級別是百萬級別,咱們驚訝的發現ArrayList插入效率要比LinkedList快接近20倍,爲何?why?咱們明明記得在學習java集合的時候,明確的知道是ArrayList查詢快,增刪慢的,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();
    }
}

複製代碼

此時咱們發現,在萬級別LinkedList的插入性能就看出來了

此時咱們發現,在十萬級別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倍 數組

數據量十萬級別Linkedlist插入效率比ArrayList慢160+倍

下面讓我係統測試一下其餘數量級數據,而且加上隨機插入測試bash

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());

    }
複製代碼

測試結果數據結構

  • 在集合頭部插入實驗數據 dom

  • 在集合中間插入實驗數據源碼分析

  • 在集合尾部插入實驗數據 性能

  • 在集合隨機位置插入數據學習

源碼分析

在指定位置插入

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能夠實現隊列,棧等數據結構,這是它的優點
相關文章
相關標籤/搜索