一、LinkedList简介
LinkedList的数据结构为双向链表结构,链表数据结构的特点是每个元素分配的空间不必连续,所以它的特点是插入和删除数据的效率非常高,但随即访问元素的效率很低,因为链表没有下标,所以只能通过遍历来访问元素。结构如下图所示。
二、LinkedList类的注解
在读源码之前,我们来看看LinkedList类上都注释了一些什么内容。
1)LinkedList是List和Deque接口的双向链表实现。实现所有可选列表操作,并允许所有元素(包含null)。
2)对于双向链表,所有操作都可以预期。索引到列表中的操作将从开头或结尾遍历列表,以较接近制定索引为准。
3)请注意,此实现不同步。如果多个线程同时访问链表,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。可以使用List list = Collections.synchronizedList(new LinkedList(…));方法来防止意外对列表的非同步访问。
4)这个类的iterator和listIterator方法返回的迭代器是快速故障的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法,迭代器将抛出ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒着任意的、不确定的行为的风险。注意,并不能保证迭代器的快速故障行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。故障快速迭代器以最大的努力抛出ConcurrentModificationException。因此,编写一个依赖于此异常来判断其正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。
总结:从以上注释中可以看出,LinkedList是双向链表结构、可以包含null元素、只能通过遍历来访问元素、LinkedList是非线程安全的(可以通过List list = Collections.synchronizedList(new LinkedList(…));方法来防止意外对列表的非同步访问)。
三、源码分析
1)类的继承实现关系
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList继承了AbstractSequentialList类并实现了List、Deque(双端队列)接口。
2)内部类
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
内部类Node就是实际的元素节点,用来存放实际元素的地方。
3)构造方法
//无参构造方法
public LinkedList() { }
//有参构造方法(将collections集合中的元素全部添加进LinkedList双向链表中)
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
4)成员变量
transient int size = 0;//链表元素数量
transient Node<E> first;//链表的第一个元素
transient Node<E> last;//链表的最后一个元素
5)链接e作为第一个元素
private void linkFirst(E e){
final Node<E> f = first;//将第一个元素赋值给f
final Node<E> newNode = new Node<>(null, e, f);//newNode为第一个节点,那么prev为null,item为e元素,next为原来的fist节点
first = newNode;//将新节点设置为first(第一个元素)
if (f == null)//如果first为null,那么表示链表为空,则newNode既是第first元素又是last元素。
last = newNode;
else //如果first不为null,那么表示链表不为空且含有其他元素,则将原来的first元素的prev元素设置为newNode(第一个元素)
f.prev = newNode;
size++;//链表的大小+1
modCount++;链表的修改次数+1
}
6)链接e作为最后个元素
void linkLast(E e) {
//将尾部节点赋值给I
final Node<E> l = last;
//创建新的节点,由于是最后一个节点,所以next为null
final Node<E> newNode = new Node<>(l, e, null);
//将新节点newNode赋值为last
last = newNode;
if (l == null)//如果I为null,那么就表示该链表为空,需要将新节点赋值给first和last
first = newNode;
else //如果链表不为空,将原来last节点的next设置为新节点,由新节点来代替last节点
l.next = newNode;
size++;//链表的大小+1
modCount++;链表的修改次数+1
}
7)在非null节点之前插入一个节点
void linkBefore(E e, Node<E> succ) {//将节点e插入在succ节点之前
// assert succ != null;
//将succ节点的perv赋值给pred
final Node<E> pred = succ.prev;
//创建新节点,将perv设置成perd,next设置为succ
final Node<E> newNode = new Node<>(pred, e, succ);
//将succ节点的perv设置为新节点
succ.prev = newNode;
if (pred == null)//如果succ节点的perv为null,那么说明succ为第一个节点,newNode插入在succ之前,那么需要设置newNode为first节点
first = newNode;
else //如果succ不为first节点,那么需要要将perd(原succ节点的perv)的next设置为newNode
pred.next = newNode;
size++;//链表的大小+1
modCount++;链表的修改次数+1
}
8)删除链接非空的第一个节点f。
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
//将f节点赋值给element
final E element = f.item;
//将f节点的next节点赋值给next
final Node<E> next = f.next;
//将f节点设置为null
f.item = null;
//将f节点next元素设置为null
f.next = null; // help GC方便GC,垃圾回收
//将原first节点的next元素设置为first节点
first = next;
if (next == null)//如果next为null,则说明该连表只有一个元素,去除之后,链表为空链表
last = null;
else //如果链表有多个元素,则需要将next的perv设置为null,由next代替原来的first节点
next.prev = null;
size--;//链表数量-1
modCount++;//链表操作次数+1
return element;//将节点数据返回
}
9)删除链表中的某一个节点
E unlink(Node<E> x) {
// assert x != null;
//将x元素的item、perv、next元素都赋值出来
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//判断x元素是否为first节点
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;//将x节点的perv设置为null
}
//判断x元素是否为last节点
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;//将x节点的next设置为null
}
//将x节点本身设置为null
x.item = null;
size--;//链表大小-1
modCount++;//链表操作次数+1
return element;//返回x节点元素
}
10)在链表中插入集合
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//验证index是否合法,index必须>=0以及<=size
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;//pred:perv,succ:item
if (index == size) {//如果index为size,那么就是在链表的末尾插入集合
succ = null;
pred = last;
} else {//在链表的中间index处插入集合
succ = node(index);
pred = succ.prev;
}
//将需要插入集合中的每一个元素都链接起来,构造成双向链表
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)//如果perd为null,则说明,原链表为空链表
first = newNode;
else//原链表不为空
pred.next = newNode;
pred = newNode;
}
//判断是否在链表末尾的地方进行插入集合
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;//链表的大小+集合的大小
modCount++;//修改次数+1
return true;
}
11)清空链表
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
//通过遍历的方式,将所有的节点都设置为null
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
12)获取链表的第index个元素
Node<E> node(int index) {
// assert isElementIndex(index);
//size >> 1是位移运算,相当于size/2,由于LinkedList是双向链表结构,没有下标,获取其中的元素时只能通过遍历的方式来获取,可以从头部开始遍历,也可以从尾部开始遍历,这里判断index是否小于size/2的目的是为了得出index更靠近头部还是尾部,然后从更近的一方开始遍历,提高了遍历的效率
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
以上只给出了LinkedList常用方法源码的解读。
这是小生第一次写博客,各位大佬多多关照,有错误之处请指出。