首先,ArrayList 是基于数组实现的,LinkedList 是基于链表实现的。
从源码来看:
ArrayList 实现了 RandomAccess 接口,所以它支持随机访问,可以直接通过下标直接获取元素。
LinkedList 没有实现 RandomAccess 接口,所以它不支持随机访问。
从内存占用来看
ArrayList 基于数组实现,是一块连续的内存空间,仅存储对象的引用,空间利用率高;如果涉及到扩容,就会重新分配内存,空间是原来的 1.5 倍,会存在一定的空间浪费。
LinkedList 基于链表实现,每个节点都需要额外储存 prev
和 next
指针,相较于 ArrayList 每个节点占用的内存空间更大,空间利用率低。内存会动态增长,没有扩容问题。
从用途上来看
ArrayList 基于数组实现,get(index)
通过下标快速访问元素,时间复杂度是O(1)。如果是增删数组的尾部,那么直接插入或删除即可,时间复杂度是O(1),但如果是增删中间的位置,由于需要移动改动位置的后面所有元素,并且有可能触发扩容,时间复杂度是O(n)。如果 add 过程中触发扩容时间复杂度也是O(n)。
LinkedList 基于链表实现,需要获取元素则需要遍历链表,时间复杂度是O(n)。增删任何节点,都只需要改变前置节点和后置节点以及插入节点的引用即可。不需要移动元素,如果是在链表的头尾部插入或删除,时间复杂度是O(1);如果插入删除链表中间的节点,因为需要遍历链表找到对应的插入/删除节点,时间复杂度为O(n)。
使用场景有什么不同?
ArrayList 适用于:
- 随机访问频繁:需要频繁通过索引访问元素的场景。
- 读取操作远多于写入操作:如存储不经常改变的列表。
- 末尾添加元素:需要频繁在列表末尾添加元素的场景。
LinkedList 适用于:
- 频繁插入和删除:在列表中间频繁插入和删除元素的场景。
- 不需要快速随机访问:顺序访问多于随机访问的场景。
- 队列和栈:由于其双向链表的特性,LinkedList 可以高效地实现队列(FIFO)和栈(LIFO)。
最后用一个表格总结:
类别 | 本质 | 用途 | 是否支持随机访问 | 内存占用 | 使用场景 |
---|---|---|---|---|---|
ArrayList | 基于数组 | 通过下标实现快速查找元素 | 实现 RandomAccess 接口,可随机访问 | 占用一块连续的空间,仅供存储对象引用,空间利用率高;扩容时会扩至 1.5 倍,可能造成空间浪费。 | 可以通过索引快速访问元素,故而可以保证随机访问频繁;插入和删除的操作效率低查找,适用于读操作远多于写操作。 |
LinkedList | 基于链表 | 通过改变链表的 next 和 prev 实现插入和删除 | 没有实现 RandomAccess 接口,不可以随机访问 | 每个节点需要添加 next 和 prev 指针,占用更富多内存,空间利用率较低;内存动态增长,无需考虑扩容问题。 | 链表只需要修改 next 和 prev ,适合频繁插入和删除;随机访问需要遍历链表效率低,故而适用于不需要快速随机访问;由于双向链表的特性,适用于需要实现队列和栈。 |