Java中的LinkedList的底层是由一个双向链表维护的,且包含头指针和尾指针。首先LinkedList是一个类,要了解一个类就需了解类的组成部分(成员变量、成员方法、类变量、构造方法及它们的访问权限如何,继承了什么类、实现了哪些接口)。下面对LinkedList主要的组成部分进行源码剖析:
1、LinkedList的家庭背景
从源码中可看到LinkedList继承了AbstractSequentialList,实现了List、Deque、Cloneable、Serializable接口。
2、LinkedList的节点Node
LinkedList类中有一个私有静态内部类Node,这就是双向链表的节点,该节点包含用于存数据的item、指向前一节点的指针prev和指向后一节点的指针next,源码如下:
3、LinkedList的成员变量
LinkedList有三个成员变量,且它们都被transient修饰,transient修饰符的作用就是在类被序列化时被transient修饰的不会被序列化。三个成员变量依次为size(记录链表节点的个数)、first(指向链表的第一个节点)、last(指向链表的最后一个节点)。源码如下:
4、LinkedList主要成员方法
一、插入
(1)、linkFirst(插入到头结点之前)
/**
* Links e as first element.
* 将e作为链表的第一个节点
*/
private void linkFirst(E e) {
//先取到该链表的第一个节点,后面会用到
final Node<E> f = first;
//新建一个节点,即要插入的节点
final Node<E> newNode = new Node<>(null, e, f);
//让first指向新的节点
first = newNode;
//是否需更新尾指针
if (f == null)//说明该链表是空的,更新尾指针
last = newNode;//新添加的节点也是最后一个节点
else//不为空,把原先的first即f的prev指向新节点就好
f.prev = newNode;
size++;
modCount++;
}
(2)、linkLast(插入到尾节点之后)
/**
* Links e as last element.
* 将e作为最后一个节点
*/
void linkLast(E e) {
//先取到该链表的最后一个节点,后面会用到
final Node<E> l = last;
//新建一个节点,即要插入的节点
final Node<E> newNode = new Node<>(l, e, null);
//更新last
last = newNode;
//是否需要更新first
if (l == null)//说明该链表是空的,更新first
first = newNode;
else//不为空,把原先最后一个节点的next指向新节点就好
l.next = newNode;
size++;
modCount++;
}
(3)、linkBefore(把节点插入到中间)
/**
* Inserts element e before non-null Node succ.
* 将e插入到succ之前,succ不为null,也就是说succ就是要插入的位置
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;断言该succ不为null
//取到succ的前一个节点
final Node<E> pred = succ.prev;
//新建一个节点,即要插入的节点
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)//说明succ是头节点
first = newNode;//更新头节点
else//让原先succ的前一个节点的下一个节点指向新节点
pred.next = newNode;
size++;
modCount++;
}
二、删除
(1)、unlinkFirst(删除头节点)
/**
* Unlinks non-null first node f.
*/
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
//先取到头节点的item用于返回值
final E element = f.item;
//取得头节点的下一个节点
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
//更新头节点
first = next;
if (next == null)//说明该链表只有一个节点就是头节点,
//现在又把它删除了,所以链表为空,last也要为null
last = null;
else//next就作为了新的头节点
next.prev = null;
size--;
modCount++;
return element;
}
(2)、unlinkLast(删除尾节点)
/**
* Unlinks non-null last node l.
*/
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
//先取到尾节点的item用于返回值
final E element = l.item;
//取得尾节点的上一个节点
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
//更新尾节点
last = prev;
if (prev == null)//说明该链表只有一个节点就是尾节点,
//现在又把它删除了,所以链表为空,first也要为null
first = null;
else//将prev的next置为null就行,prev就成了新的尾节点
prev.next = null;
size--;
modCount++;
return element;
}
(3)、unlink(删除中间节点)
/**
* Unlinks non-null node x.
*/
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {//说明节点x是头节点
first = next;//将first指向x的下一个节点即可
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {//说明节点x是尾节点
last = prev;//将last指向x的上一个节点即可
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
LinkedList还有许多public的方法供我们调用,这些方法的内部实现基于上面这六个private或default方法,如add方法,源码如下:
调用了linkLast方法,将e添加到最后。
这里就不一一举例了,有想了解更多LinkedList方法的同学可以自己去看看源码,只要掌握以上六个方法的原理,其他方法迎刃而解。
总结: LinkedList是一个带头尾指针的双向链表结构,优势就在于它的插入和删除操作快捷方便。LinkedList实现了Deque接口,而Deque又继承了queue接口,故LinkedList也是queue(队列)的实现类,可实现先进先出的队列功能,当也可实现后进先出的栈功能。