ArrayList與LinkedList效率對比


概述

  1. ArrayList 是一個動態數組,它是線程不安全的,允許元素為null。其底層數據結構依然是數組,因為實現了RandomAccess接口,所以擁有隨機快速訪問的能力,ArrayList可以以O(1)的時間復雜度去根據下標訪問元素。由於數組的內存連續,可以根據下標以O1的時間改查元素,因此時間效率很高
  2. LinkedList 是一個雙向鏈表,它是 線程不安全的,允許元素為null。其底層數據結構是鏈表,和ArrayList比,沒有實現RandomAccess接口,所以其以下標,隨機訪問元素速度較慢。但它的增刪只需要移動指針即可,故時間效率較高

總體而言,ArrayList的改查效率高,LinkedList的增刪效率高,真實情況下是否如此呢?

ArrayList 、LinkedList效率對比

1.插入

	/**
     * 順序插 
     */
    @Test
    public void test() {
        ArrayList<String> arr = new ArrayList<>();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 200000; i++) {
            arr.add("a");
        }
        long end = System.currentTimeMillis();
        System.out.println("arrylist time:" + (end - start));


        LinkedList<String> link = new LinkedList<>();
        start = System.currentTimeMillis();
        for (int i = 0; i < 200000; i++) {
            link.addLast("a");
        }
        end = System.currentTimeMillis();
        System.out.println("linkedlist time:" + (end - start));

    }

可以發現,結果與我們預計的相同。LinkedList插入速度快於ArrayList。

但是,我們將插入的數值加大到100w。

ArrayList的插入速度居然快於LinkedList,這是為什么呢?

因為ArrayList每次需要擴容的話,新數組是舊數組容量的1.5倍,再使用Arrays.copyOf()將舊數組中的元素復制到新數組,不需要擴容的話則直接插入。插入數據的大部分時間用來復制數組。

而LinkedList每次添加元素需要構建節點,再將新節點插入隊尾。插入數據的大部分時間用來構建節點。

當元素很少的時候,構建節點所需的時間少於數組復制的時間,但是當元素急劇增多的時候,ArrayList數組擴容到足夠大,已經足夠添加所有元素,此時不需要再次擴容,所以幾乎沒有復制數組的時間。而LinkedList鏈表添加時間隨着元素的不斷增多而增多。

讓我們再測試各個位置添加元素的效率

/**
 * 頭插入 array < linked
 */
@Test
public void test1() {
    ArrayList<String> arr = new ArrayList<>();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 200000; i++) {
        arr.add(0, "a");
    }
    long end = System.currentTimeMillis();
    System.out.println("arrylist time:" + (end - start));


    LinkedList<String> link = new LinkedList<>();
    start = System.currentTimeMillis();
    for (int i = 0; i < 200000; i++) {
        link.add(0, "a");
    }
    end = System.currentTimeMillis();
    System.out.println("linkedlist time:" + (end - start));

}

這是因為ArrayList每次插入新元素都要將后面所有元素向后移動一位,這是ArrayList插入最壞的情況。

	/**
     * 中間插  ArrayList 》》 LinkedList
     */
    @Test
    public void test3() {
        ArrayList<String> arr = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            arr.add("a");
        }
        LinkedList<String> link = new LinkedList<>();
        for (int i = 0; i < 100000; i++) {
            link.add("a");
        }
        int n = arr.size() / 2;

        long startlist = System.currentTimeMillis();

        for (int i = 0; i < 10000; i++) {
            arr.add(n + i, "a");
        }

        long endlist = System.currentTimeMillis();
        System.out.println("arr time===" + (endlist - startlist));
        n = link.size() / 2;

        long startLink = System.currentTimeMillis();

        for (int i = 0; i < 10000; i++) {
            link.add(n + i, "a");
        }
        
        long endLink = System.currentTimeMillis();
        System.out.println("link time -----" + (endLink - startLink));

    }

可以看到只是插入萬級別的數據,差距已經很大了。

LinkedList在指定位置插入需要先移動移動指針到指定位置,雖然將鏈表分成前后兩部分進行查找,但是速度明顯慢於數組,這在隨機插入中表現更加明顯。

/**
 * 隨機插入 array >> linked
 */
@Test
public void test4() {
    List<String> list = new ArrayList<>();
    Random random = new Random();
    long start = System.currentTimeMillis();
    for (int i = 1; i < 50000; i++) {
        int x = random.nextInt(i);
        list.add(x, "a");
    }
    long end = System.currentTimeMillis();
    System.out.println("arrayList insert time " + (end - start));


    List<String> list1 = new LinkedList<>();
    start = System.currentTimeMillis();
    for (int i = 1; i < 50000; i++) {
        int x = random.nextInt(i);
        list1.add(x, "a");
    }
    end = System.currentTimeMillis();
    System.out.println("linkedList insert time " + (end - start));
}

只是插入5w數據,差距已經如此明顯。

綜上所述,除了經常進行頭插入的情況,一般情況下插入優先考慮使用ArrayList。

2.讀取

ArrayList擁有快速訪問能力,不管是隨機讀還是順序讀,效率都高於LinkedList。

/**
 * 隨機讀 array > linked
 */
@Test
public void test6() {
    List<String> arr = new ArrayList<>();
    Random random = new Random();
    for (int i = 0; i < 200000; i++) {
        arr.add("a");
    }
    List<String> linked = new LinkedList<>();
    for (int i = 0; i < 200000; i++) {
        linked.add("a");
    }
    long start = System.currentTimeMillis();
    for (int i = 1; i < arr.size(); i++) {
        int x = random.nextInt(i);
        arr.get(x);
    }
    long end = System.currentTimeMillis();
    System.out.println("arrayList insert time " + (end - start));

    start = System.currentTimeMillis();
    for (int i = 1; i < linked.size(); i++) {
        int x = random.nextInt(i);
        linked.get(i);
    }
    end = System.currentTimeMillis();
    System.out.println("linkedList insert time " + (end - start));
}
/**
 * 順序刪 array 》 linked
 */
@Test
public void test7() {
    ArrayList<String> arr = new ArrayList<>();
    for (int i = 0; i < 20000; i++) {
        arr.add("a");
    }
    long start = System.currentTimeMillis();
    for (int i = 0; i < arr.size(); i++) {
        arr.remove(i);
    }
    long end = System.currentTimeMillis();
    System.out.println("arrylist time:" + (end - start));

    LinkedList<String> link = new LinkedList<>();
    for (int i = 0; i < 20000; i++) {
        link.add("a");
    }
    start = System.currentTimeMillis();
    for (int i = 0; i < link.size(); i++) {
        link.remove(i);
    }
    end = System.currentTimeMillis();
    System.out.println("linkedlist time:" + (end - start));
}

3.刪除與插入同理,不管是隨機刪還是順序刪,ArrayList 的效率都要高於LinkedList。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM