ArrayList 内部是顺序表结构,地址是连续的,是连续存储;LinkedList内部是链表结构,地址是随机的,通过next引用来链接下个地址,是离散存储。LinkedList 底层是链表(双向链表),链表是离散存储的,为了能把相关联的数据连续起来,那么实现链表就必须在前一块数据的存储空间中记录下下一块的数据存储空间的地址,这样才能达到所有的数据环环相扣,联系起来成为一个整体。所以ArrayList的随机访问能力很强,相当于“闪现”。
从增删改查四个方面看。
插入数据
从头插入元素: ArrayList < LinkedList
_________________O(n)_______O(1)
从中间插入元素:
时间复杂度都是O(n), LinkedList在添加元素的时候,首先会通过循环查找到添加元素的位置,如果要添加的位置处于 List 的前半段,就从前往后找;若其位置处于后半段,就从后往前找。ArrayList需要进行元素搬运。
从尾添加效率: 再不考虑扩容情况下,ArrayList的尾插时间复杂度O(1),不需要搬运数组。
LinkedList时间复杂度O(1),但LinkedList 中多了 new 对象以及变换指针指向对象的过程,所以效率要低于 ArrayList(基于 ArrayList 初始化容量足够,排除动态扩容数组容量的情况下)。
查找
已知位置找值:
ArrayList的get方法,时间复杂度O(1)【随机访问能力】
LinkedList的get方法,时间复杂度O(n),需要循环。
已知值找位置:
ArrayList和LinkedList时间复杂度都是O(n),都需循环遍历。
在遍历LinkedList时,最好用迭代循环(实现了lterable接口for-each)而不是for,因为LinkedList底层是基于双向链表实现的,如果我们使用for循环历遍:LinkedList在get任何一个位置的数据的时候,都会把前面的数据走一遍。
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
使用迭代器呢,它是每取一个元素就把游标指向下一个节点,根据游标就可以得到下一个元素,因此不用像for循环一样那么耗时。
删除
ArrayLIst,查询时间复杂度O(1);删除后该位置后面所有元素要前移,时间复杂度O(n)
LinkedList,查询时间复杂度为O(N);删除时间复杂度为O(1)
修改
ArrayList——O(1)
LinkedList——O(n) ,需遍历数组。
总结
类似于插入数据,删除数据时, LinkedList 也优于 ArrayList 。
修改和查找时,ArrayList则优于LinkedList。
什么场景下更适宜使用 LinkedList,而不用ArrayList
1.你的应用不会随机访问数据 。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据。
2.你的应用更多的插入和删除元素,更少的读取数据 。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。