List 本身的特点是有序,可重复,支持索引。它有三种常用的实现类
ArrayList
- 在这其中,ArrayList 是最常用的实现类,其底层维护了一个 Object 类型的数组以存放数据
transient Object[] elementData; // 底层数组定义代码
- 使用无参构造器创建 ArrayList 时,elementData 初始容量为 0 ,第一次添加元素时扩容为 10 ,如需再次扩容,则扩容为当前容量的 1.5 倍
- 如果使用的是指定大小的构造器,elementData 初始容量为指定大小,如需扩容,则扩容为当前容量的 1.5 倍
- 扩容时底层使用的是 Arrays.copyof() ,很耗费时间。所以我们一般会给一个合适的初始容量,过大浪费空间,过小经常扩容浪费时间
Vector
- 相较于 ArrayList 最大的特点是线程安全,其余基本等同
- 无参构造 Vector 时,elementData 初始容量直接为 10 ;有参构造就是指定初始容量。如需扩容,则扩容为当前容量的 2 倍
LinkedList
- 底层维护了内部类 Node 作为其节点,Node 中维护了 prev 指向前一个 Node 对象、next 指向后一个,item 维护数据。以此构成了类似于双向链表的数据结构,在增删时只需要维护 Node 指向即可
ArrayList 与 LinkedList 的比较
这里就比较重要了,我们在学习数据结构时常常被问到数组和链表的优势——数组改查快、链表增删快
但上述结论在 ArrayList 和 LinkedList 的比较中并没有体现出来。无论是增删还是改查,永远都是 ArrayList 的效率高于 LinkedList ,而 ArrayList 和 LinkedList 的后部增删效率都是高于前部的
这就比较奇怪,ArrayList 的后部效率高于前部可以理解,后部元素增删时需要移位的元素较少,但为何 LinkedList 的后部效率高于前部我就不得而知了,我也不愿深究,因为我在工作中是不会使用 LinkedList 的
下面为我的测试代码,供各位参考
public class Main {
public static void main(String[] args) {
// List 元素个数:1千万
int size = 10000000;
// ArrayList 增删速度
System.out.println("arrayList---------------------");
listSpeed(size, new ArrayList());
// LinkedList 增删速度
System.out.println("linkedList--------------------");
listSpeed(size, new LinkedList());
}
public static void listSpeed(int size, List list) {
// 构建 List
for (int i = 0; i < size; i++) {
list.add(i);
}
// 开始时间
long start = System.currentTimeMillis();
// 测试内容1:对 List 中部之前进行 1000 次的随机增删
for (int i = 0; i < 1000; i++) {
int random = RandomUtil.randomInt(5000000);
list.remove(random);
list.add(random, random);
}
// 结束时间
long end1 = System.currentTimeMillis();
// 测试结果:单位s
System.out.println((end1 - start) / 1000.0); // 5.342 36.712
// 测试内容2:对 List 中部之后进行 1000 次的随机增删
for (int i = 0; i < 1000; i++) {
int random = RandomUtil.randomInt(5000000, 10000000);
list.remove(random);
list.add(random, random);
}
// 结束时间
long end2 = System.currentTimeMillis();
// 测试结果:单位s
System.out.println((end2 - end1) / 1000.0); // 0.863 14.396
}
}