Java LinkedList源码分析
首先我们看下链表的数据结构
从上面可以看出,链表是一个节点连着一个节点的。节点包含三个属性,分别为他的前缀、后缀、以及节点的值。链表是通过节点的next属性进行连接的,这样就形成了我们看到的逻辑结构。注意:当链表中只要一个节点的时候,链表的头结点和尾结点都是这个节点
链表的定义:链表中相邻的两个结点分别称为前驱结点和后继结点,前驱结点的指针域存放其后继结点的
地址 前驱节点的指针域指向后继结点
链表中最特殊的就是头结点和尾结点
头结点:单向链表中没有前驱结点的结点
尾结点:单向链表中没有后继结点的结点
那么接下来我们一起来分析下LinkedList的源码,我们只做一个简单的分析
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0; //链表的长度,默认是0
transient Node<E> first; //链表的头结点,默认为null
transient Node<E> last; //链表的尾结点,默认为null
private static class Node<E> { //在链表中定义了一个内部类,链表的节点Node类
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;
}
}
//链表的新增方法
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
//链表中根据索引下标去获取值
public E get(int index) {
checkElementIndex(index); //代码健壮性操作,这里我们就不进行分析了
return node(index).item; //重点在于这个方法,找到匹配节点之后,把节点的item属性返回出去
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { //先对传过来的index值进行判断,对index和链表长度一半进行比较,看要查的这个索引是在链表的上半部分还是下半部分。这是源码对于查找进行的一个优化,可以减少一半的循环。假如数据为100W,这个操作就能减少50W次的循环。
Node<E> x = first; //在上半部分,就创建一个节点,将首节点给这个X,从头节点开始找
for (int i = 0; i < index; i++) //循环index
x = x.next; //一直将节点的下一个节点赋值给x节点,直到找到
return x;
} else {
Node<E> x = last; //下半部分是从尾结点开始找
for (int i = size - 1; i > index; i--) //与前面逻辑一样,只是这个是反过来,从尾结点开始找
x = x.prev;
return x;
}
}
}
对链表新增方法进行逐行分析
对链表通过索引index,获取值方法进行逐行分析
//链表中根据索引下标去获取值
public E get(int index) {
checkElementIndex(index); //代码健壮性操作,这里我们就不进行分析了
return node(index).item; //重点在于这个方法,找到匹配节点之后,把节点的item属性返回出去
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { //先对传过来的index值进行判断,对index和链表长度一半进行比较,看要查的这个索引是在链表的上半部分还是下半部分。这是源码对于查找进行的一个优化,可以减少一半的循环。假如数据为100W,这个操作就能减少50W次的循环。
Node<E> x = first; //在上半部分,就创建一个节点,将首节点给这个X,从头节点开始找
for (int i = 0; i < index; i++) //循环index
x = x.next; //一直将节点的下一个节点赋值给x节点,直到找到
return x;
} else {
Node<E> x = last; //下半部分是从尾结点开始找
for (int i = size - 1; i > index; i--) //与前面逻辑一样,只是这个是反过来,从尾结点开始找
x = x.prev;
return x;
}
}