Java Collection 框架 LinkedList 源码解读 与 实现原理 浅析
上一篇我们简单了解了ArrayList的底层原理,当插入与删除时,可能每次都需要移动其底层整个数组内的元素,速度通常很慢,但是好处也很明显通过数组结构进行访问查找时,通过指针可以快速定位元素。
今天继续来看一下LinkedList的底层原理。
LinkedList本质上是一个双向链表,与ArrayList对比,LInkedlist插入与删除速度快,但是访问速度慢,是按照链路顺序查找的线性结构,可以将零散的内存串联起来,内存利用效率高。
接下来看一下LInkedList的源码,看看LinkedList的底层数据结构是什么样子的。
LinkedList包含了三个成员变量,都用transient关键字来使其在序列化时被忽略,size指的是尺寸大小,first记录链表的第一个节点Node,last记录链表的最后一个节点Node,与ArrayList不同,LinkedList
不是以数组为底层数据结构,而是使用Node节点,接下来我们来看一下LinkedList记录元素的基本单位Node类。
Node类是一LinkedList的一个内部私有类,内部有三个成员变量,item即需要存储的元素本身,next为当前节点的下一个节点,perv为当前节点的前一个节点,Node的构造器为存储这三个参数。通过next与perv,LinkedList便可以让Node节点串联起来。
接下来我们看一下LinkedList的几个特有方法:
addFirst();
addFirst在头部添加一个节点。
实际上调用了linkFirst的方法。
取出第一个节点赋值给f,创建新节点,参数perv节点为空,将f当做新节点的next节点,并把创建的新节点赋值给LinkedList的first变量。
如果f为空,则LinkedList为空链表,将last节点也指向newNode。如果f不为null,就将f的前一个节点指向newNode。size++。
addLast();
addLast实际调用了linkLast(e);
原理同linkFirst,这里不再赘述。
offerFirst()与offerLast()
JDK1.6之后加入了offerFirst();与offerLast(),调用了addFirst与addLast,底层还是调用linkFirst与linkLast,添加了返回布尔值。
接下来看一下LinkedList对于add,set,remove,get接口方法的实现。
public boolean add(E e)
public void add(E e)方法,实际上调用了linkLast,向链表的尾部添加元素。
public boolean remove(Object o)
remove方法实际上为for循环遍历链表找到指定元素后执行unlink方法。
unlink方法,取指定节点Node,然后指定节点如果没有prev,则执行其next为first。如果prev不为null则将前一个节点的next指定为指定节点的next节点,这样来使要被remove的节点的前后两个节点链接起来。
如果next为null则执行将last指向被删除Node的prev,如果不为null则将下一个被删除Node的下一个节点的prev指向被删除节点Node的prev保证链接。
最后将x.item赋值为null,size尺寸减一。最后返回被删除节点的元素。
再来看removeFirsr与removeLast方法。调用了unlinkFirst与unlinkLast。
原理同上不再赘述,这里粘出源码,大家可以自己读一下。接下来我们看一下get方法。
get方法需要传入index指针首先会先check指针的合法性,然后调用node(index)方法获取元素。
这个方法实际上就是遍历整个链表,但是这里会根据index来判断距离头部节点近还是距离尾部节点近来决定从头还是尾进行遍历查找。
在使用get方法的时候,index从哪里来呢?所以这里还要使用public int indexOf(Object o)来获取节点指针。
同样通过for循环遍历计算index,所以说在使用查询和查找Linkedlist元素时,效率没有ArrayList高,因为需要遍历整个链表。
JDK1.5之后加入了peek,poll,element,remove方法,用于获取头部和,或者获取后删除元素,实际上这是Queue队列性质的操作。
JDK1.6加入了Deque operations双端队列操作,这些方法的基础方法上面都已经说过了,这里源码比较简单就不再赘述。
LinkedList最大的好处在于头尾和已知节点的插入和删除时间复杂度都是o(1)只需要一次操作即可。
但是涉及到先确定位置再操作的情况,则时间复杂度会变为o(n)线性级,随着数据的增多,耗时也会增加。
此外,每个节点都需要保留prev和next指针也浪费了空间。
LinkedList本身也有迭代器的实现,原理与上述方法差不多,这里感兴趣可以自行查看源码,也是对于链表和节点的操作,这里不再赘述。
以上就是关于LInkedList底层原理的浅析。