
【Java容器源码】LinkedList源码分析
1.结构
LinkedList 继承关系,核心成员变量,主要构造函数:
2.方法解析&api
2.1 尾插
追加节点时,我们可以选择追加到链表头部,还是追加到链表尾部,add 方法默认是从尾部开始追加,addFirst 方法是从头部开始追加,我们分别来看下两种不同的追加方式:
add()
linkLast()
尾插的核心逻辑如下:
newNode.pre = last
last.next = newNode (注:考虑last=null情况(链表为空,这时仅更新头结点即可))
last = newNode
2.2 头插
要对LinkedList头插时是调用addFirst方法
linkFirst()
头插核心逻辑如下:
newNode.next = first;
first.prev = newNode; (注:考虑first=null(链表为空,只用更新last即可))
first = newNode;
2.3 删除指定元素
节点删除的方式和追加类似,我们可以删除指定元素,或者从头部(尾部)删除,删除操作会把节点的值,前后指向节点都置为 null,帮助 GC 进行回收
remove()
删除指定元素;该方法在此处的作用是找到要删除的节点。注意,只有链表有这个节点且成功删除才返回true
注:remove还可以根据索引删除
unlink()
删除的核心逻辑如下:
x.prev.next = x.next (注:考虑x.prev=null(x是first,直接更新first))
x.next.prev = x.prev.prev (注:考虑x.next=null(x是last,直接更新last))
2.4 删除头节点
remove()
删除头节点,队列为空时抛出异常。这里注意,与删除指定元素时需要传入一个参数,而删除头节点时为空参。
removeFirst()
判断当前链表时否为空
unLinkFirst()
执行删除头节点,具体删除逻辑如下
first.next.pre = null;(注:考虑first=null(链表为空), first.next=null(尾结点,即链表仅一个节点))
first = first.next;
从源码中我们可以了解到,链表结构的节点新增、删除都非常简单,仅仅把前后节点的指向修改下就好了,所以 LinkedList 新增和删除速度很快。
2.5 查询
链表查询某一个节点是比较慢的,需要挨个循环查找才行,我们看看 LinkedList 的源码是如何寻找节点的
get()
根据索引进行查找
node()
从源码中我们可以发现,LinkedList 并没有采用从头循环到尾的做法,而是采取了简单二分法,首先看看 index 是在链表的前半部分,还是后半部分。如果是前半部分,就从头开始寻找,反之亦然。通过这种方式,使循环的次数至少降低了一半,提高了查找的性能,这种思想值得我们借鉴
3.迭代器
因为 LinkedList 要实现双向的迭代访问,所以使用 Iterator 接口肯定不行了,因为 Iterator 只支持从头到尾的访问。Java 新增了一个迭代接口,叫做:ListIterator,这个接口提供了向前和向后的迭代方法,如下所示:
listIterator()
返回迭代器,可以传入index,表示从指定节点开始迭代,可前可后
3.1 从前向后迭代
hasNext()
判断还有没有下一个元素,还是通过index和size控制
next()
取下一个元素,并后移
3.2 从后向前迭代
hasPrevious()
如果上次节点索引位置大于 0,就还有节点可以迭代
previous()
3.3 删除:remove
迭代时,删除当前元素
4.Queue的实现
LinkedList 实现了 Queue 接口,在新增、删除、查询等方面增加了很多新的方法,这些方法在平时特别容易混淆,在链表为空的情况下,返回值也不太一样,下面列一个表格,方便大家记录:
PS:Queue 接口注释建议 add 方法操作失败时抛出异常,但 LinkedList 实现的 add 方法一直返回 true。
LinkedList 也实现了 Deque 接口,对新增、删除和查找都提供从头开始,还是从尾开始两种方向的方法,比如 remove 方法,Deque 提供了 removeFirst 和 removeLast 两种方向的使用方式,但当链表为空时的表现都和 remove 方法一样,都会抛出异常。
作者:A minor
来源:CSDN
