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,在大数据量的情况下,其遍历速度将慢得令人发指
整理自: