LinkedList
提到ArrayList,就会比较与LinkedList的区别。本文来看看LinkedList的核心原理。
如图所示 LinkedList 底层是基于双向链表实现的,也是实现了 List 接口,所以也拥有 List 的一些特点(JDK1.7/8 之后取消了循环,修改为双向链表)。
一、LinkedList属性
1 | //链表的节点个数. |
二、Node的结构
1 | private static class Node<E> { |
三、添加元素
3.1 LinkedList表头添加一个元素
当向表头插入一个节点时,很显然当前节点的前驱一定为 null,而后继结点是 first 指针指向的节点,当然还要修改 first 指针指向新的头节点。除此之外,原来的头节点变成了第二个节点,所以还要修改原来头节点的前驱指针,使它指向表头节点,源码的实现如下:
1 | public void addFirst(E e) { |
3.2 LinkedList表尾添加一个元素
当向表尾插入一个节点时,很显然当前节点的后继一定为 null,而前驱结点是 last 指针指向的节点,然后还要修改 last 指针指向新的尾节点。此外,还要修改原来尾节点的后继指针,使它指向新的尾节点,源码的实现如下:
1 | public void addLast(E e) { |
3.3 LinkedList在指定节点前添加一个元素
1 | public void add(int index, E element) { |
可见每次插入都是移动指针,和 ArrayList
的拷贝数组来说效率要高上不少。
四、删除元素
删除操作与添加操作大同小异,例如删除指定节点的过程如下图所示,需要把当前节点的前驱节点的后继修改为当前节点的后继,以及当前节点的后继结点的前驱修改为当前节点的前驱。
就不赘述了。
五、获取元素
1 | //获取指定索引对应的元素 |
上述代码,利用了双向链表的特性,如果index
离链表头比较近,就从节点头部遍历。否则就从节点尾部开始遍历。使用空间(双向链表)来换取时间。
node()
会以O(n/2)
的性能去获取一个结点- 如果索引值大于链表大小的一半,那么将从尾结点开始遍历
这样的效率是非常低的,特别是当 index
越接近 size
的中间值时。
总结
1、理论上无容量限制,只受虚拟机自身限制影响,所以没有扩容方法。
2、和
ArrayList
一样,LinkedList
也是是未同步的,多线程并发读写时需要外部同步,如果不外部同步,那么可以使用Collections.synchronizedList
方法对LinkedList
的实例进行一次封装。
3、和
ArrayList
一样,LinkedList
也对存储的元素无限制,允许null
元素。
4、顺序插入速度
ArrayList
会比较快,因为ArrayList
是基于数组实现的,数组是事先new好的,只要往指定位置塞一个数据就好了;LinkedList
则不同,每次顺序插入的时候LinkedList
将new一个对象出来,如果对象比较大,那么new的时间势必会长一点,再加上一些引用赋值的操作,所以顺序插入LinkedList
必然慢于ArrayList
5、基于上一点,因为
LinkedList
里面不仅维护了待插入的元素,还维护了Entry
的前置Entry
和后继Entry
,如果一个LinkedList
中的Entry
非常多,那么LinkedList
将比ArrayList
更耗费一些内存
6、数据遍历的速度,看最后一部分,这里就不细讲了,结论是:使用各自遍历效率最高的方式,
ArrayList
的遍历效率会比LinkedList
的遍历效率高一些
7、有些说法认为
LinkedList
做插入和删除更快,这种说法其实是不准确的:
LinkedList
做插入、删除的时候,慢在寻址,快在只需要改变前后Entry
的引用地址ArrayList
做插入、删除的时候,慢在数组元素的批量copy
,快在寻址
所以,如果待插入、删除的元素是在数据结构的前半段尤其是非常靠前的位置的时候,
LinkedList
的效率将大大快过ArrayList
,因为ArrayList
将批量copy
大量的元素;越往后,对于LinkedList
来说,因为它是双向链表,所以在第2个元素后面插入一个数据和在倒数第2个元素后面插入一个元素在效率上基本没有差别,但是ArrayList
由于要批量copy
的元素越来越少,操作速度必然追上乃至超过LinkedList
。
从这个分析看出,如果你十分确定你插入、删除的元素是在前半段,那么就使用
LinkedList
;如果你十分确定你删除、删除的元素在比较靠后的位置,那么可以考虑使用ArrayList
。如果你不能确定你要做的插入、删除是在哪儿呢?那还是建议你使用LinkedList
吧,因为一来LinkedList
整体插入、删除的执行效率比较稳定,没有ArrayList
这种越往后越快的情况;二来插入元素的时候,弄得不好ArrayList
就要进行一次扩容,记住,ArrayList
底层数组扩容是一个既消耗时间又消耗空间的操作.
8、
ArrayList
使用最普通的for
循环遍历,LinkedList
使用foreach
循环比较快.注意到ArrayList
是实现了RandomAccess
接口而LinkedList
则没有实现这个接口.关于RandomAccess
这个接口的作用,看一下JDK API上的说法:
9、如果使用普通
for
循环遍历LinkedList
,在大数据量的情况下,其遍历速度将慢得令人发指
整理自: