ArrayList与LinkedList效率对比
概述
- ArrayList 是一个动态数组,它是线程不安全的,允许元素为null。其底层数据结构依然是数组,因为实现了RandomAccess接口,所以拥有随机快速访问的能力,ArrayList可以以O(1)的时间复杂度去根据下标访问元素。由于数组的内存连续,可以根据下标以O1的时间改查元素,因此时间效率很高。
- 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。
原文地址:https://www.cnblogs.com/le-le/p/12347303.html
时间: 2024-10-08 03:51:17