ArrayList与LinkedList效率对比

Posted le-le

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList与LinkedList效率对比相关的知识,希望对你有一定的参考价值。

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插入最坏的情况。

    /**
     * 中间插 1.数据量低于150w LinkedList 》 ArrayList
     *        2.数据量大于150w ArrayList 《 LinkedList
     */
    @Test
    public void test3() {
        ArrayList<String> arr = new ArrayList<>();
        for (int i = 0; i < 1500000; i++) {
            arr.add("a");
        }
        long start = System.currentTimeMillis();

        for (int i = 0; i < arr.size() / 2; i++) {
            arr.add("a");
        }
        long end = System.currentTimeMillis();
        System.out.println("arrylist time:" + (end - start));


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

    }

当插入的数据量是140w时

技术图片

LinkedList小胜ArrayList一筹。但当数据量增加到150w时

技术图片

LinkedList的插入所需时间急剧增多,而ArrayList所需时间基本保持不变,可以认为150w是两者的转折点。

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。

以上是关于ArrayList与LinkedList效率对比的主要内容,如果未能解决你的问题,请参考以下文章

jdk8下 ArrayList与LinedList 排序效率对比

ListSetMap下各类型的对比

Java数据结构之表的增删对比---ArrayList与LinkedList之一

JAVA中ArrayList与LinkedList的区别以及对应List使用foreach与使用下标遍历的效率问题

ArrayList 和 LinkedList的执行效率比较

Java高级面试题及答案