LinkedList 底层数组结构
底层通过双向链表实现,链表内每个节点使用node类表示,LinkedList通过first和last引用分别指向链表的第一个和最后一个元素。
注意点
- 当链表为空时,firs和last都指向null
- LinkedList 线程不安全
- LinkedList是基于链表实现的,因此不存在容量不足的问题,所以不需要扩容机制
- LinkedList还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用
源码解析
node类
//链表节点的类型
//一个对象对应一个节点
private static class Node<E> {
//元素的引用,如果为null,表示没有存储任何元素,如果不为null,表示存储了某种类型的元素
E item;
//下一个节点的引用
//引用代表了对象的十六进制地址值,所以也可以注释为:下一个节点在内存中的地址
//如果为null,可能是空链表,也可能是尾节点
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
//元素的引用初始化
this.item = element;
//下一个节点的引用初始化
this.next = next;
//上一个节点的引用初始化
this.prev = prev;
}
}
LinkendList类
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//记录当前双向链表的长度
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
//记录当前双向链表的头节点
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
//记录当前双向链表的尾节点
transient Node<E> last;
/**
* Constructs an empty list.
*/
public LinkedList() {
}
...
}
添加方法
JDK1.8开始,LinkedList集合中有3个用于在链表的不同位置添加新的节点;
private void linkFirst(E e) {
//使用一个临时的变量记录操作前first属性的信息
final Node<E> f = first;
//创建一个数据信息为e的新节点,该节点前置节点引用为null,后置节点引用指向原先的头节点
final Node<E> newNode = new Node<>(null, e, f);
//因为要在双向链表头部添加新的节点,将first属性中的信息重新设置
first = newNode;
//条件成立,说明双向链表没有任何节点
if (f == null)
//将last节点也指向新的节点,这样first和last节点属性同时指向同一个节点
last = newNode;
else
//不成立,说明双向链表至少有一个节点,只需要把原来的头节点的前置节点引用指向新的头节点
f.prev = newNode;
//双向链表长度 + 1
size++;
//linkedList集合的操作次数 + 1
modCount++;
}
private void linkLast(E e) {
//使用一个临时变量来记录操作前的last属性信息
final Node<E> l = last;
//创建一个新的节点,item属性值为e,新节点的前置对象指向原来的尾节点,后置节点为null
final Node<E> newNode = new Node<>(l, e, null);
//因为要在双向链表的尾节点添加新的节点,将last属性中的信息重新设置
last = newNode;
//条件成立,说明双向链表没有任何节点
if (l == null)
//将first节点指向新的节点,first和last都同时指向同一个节点
first = newNode;
else
//不成立,双向链表至少有一个节点,将原来的尾节点的后置节点指向新的尾节点
l.next = newNode;
//双向链表长度 + 1
size++;
//linkedList集合的操作次数 + 1
modCount++;
}
private void linkBefore(E e, Node<E> succ) {
// assert succ != null;
//创建一个变量,记录当前succ的前置节点引用(可能为null)
final Node<E> pred = succ.prev;
//创建一个新的节点,该节点的前置节点引用指向succ节点的前置节点,该节点的后置节点引用指向succ节点
final Node<E> newNode = new Node<>(pred, e, succ);
//将succ节点的前置节点重新设置为刚刚创建的新的节点
succ.prev = newNode;
//条件成立,说明当前succ节点原本就是双向链表的头节点,可以看作当前的操作其实就是在链表的头部添加一个新的节点
if (pred == null)
//这个时候将first属性的指向新创建的节点
first = newNode;
else
//不成立,将succ的前置节点的后置节点设置为当前新创建的节点
pred.next = newNode;
//双向链表长度 + 1
size++;
//linkedList集合的操作次数 + 1
modCount++;
}
删除方法
LinkedList集合中有3个移除集合中数据对象的方法;
- remove(Object O)方法
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
private E unlink(Node<E> x) {
// assert x != null;
//定义一个element变量,记录当前节点中的数据对象,以便方法最后返回
final E element = x.item;
//创建一个next节点,记录当前节点中的后置节点引用,可能为null
final Node<E> next = x.next;
//创建一个prev节点,记录当前节点中的前置节点引用,可能为null
final Node<E> prev = x.prev;
//如果条件成立,说明被移除的x节点是双向链表的头节点
if (prev == null) {
//将x的后置节点设置为新的头节点
first = next;
} else {
//将x的前置节点中的后置节点设置为移除的x节点的后置节点
prev.next = next;
//将移除的x节点的前置节点设置为null
x.prev = null;
}
//如果条件成立,说明被移除的x节点是双向链表的尾节点
if (next == null) {
//将移除的x的节点的前置节点设置为新的尾节点
last = prev;
} else {
//将x的后置节点中的前置节点设置为移除x节点的前置节点
next.prev = prev;
//将移除的x节点的后置节点设置为null
x.next = null;
}
//将移除的x节点中的数据对象设置为null
x.item = null;
//双向链表长度 - 1
size--;
//LinkedList集合操作次数 + 1
modCount++;
return element;
}
- removeFirst()方法
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
//定义一个element变量,记录当前双向链表头节点的数据对象,以便方法最后将其返回
final E element = f.item;
//创建一个next变量,记录当前双向链表头节点的后置节点引用,可能为null
final Node<E> next = f.next;
//设置当前双向链表头节点的数据对象为null,后置节点引用设置为null
f.item = null;
f.next = null; // help GC (帮助进行GC(java的垃圾回收机制))
//设置双向链表新的头节点为当前头节点的后置节点
first = next;
//条件处理,说明完成头节点的移除操作,当前双向链表已经没有任何节点
if (next == null)
//将last属性设置为null
last = null;
else
//不成立,设置新的头节点前置节点设置为null,因为新的头节点的前置节点指向是原先的头节点
next.prev = null;
//双向链表长度 - 1
size--;
//LinkedList集合操作次数 + 1
modCount++;
return element;
}
- removeLast()方法
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
//定义一个element变量记录当前双向链表尾节点中的数据对象,以便在最后将其返回
final E element = l.item;
//创建一个prev变量,记录当前双向链表尾节点中的前置节点,该变量可能为null
final Node<E> prev = l.prev;
//设置当前双向链表尾节点中的数据为null,前置对象引用为null
l.item = null;
l.prev = null; // help GC 帮助进行GC(垃圾回收)
//设置双向链表新的尾节点为当前尾节点的前置节点
last = prev;
//条件处理,说明移除完尾节点之后双向链表已经没有任何节点了
if (prev == null)
//设置头节点为null
first = null;
else
//不成立,设置新的尾节点后置节点设置为null,因为新的尾节点的后置节点指向是原先的尾节点
prev.next = null;
//双向链表长度 - 1
size--;
//LinkedList集合操作次数 + 1
modCount++;
return element;
}
双向链表查询指定的索引位的方式,是从头节点或者尾节点开始进行遍历,实现的方法在java.util.LinkedList集合中的node(int)方法
Node<E> node(int index) {
// assert isElementIndex(index);
//如果条件成立,说明当前指定的index号索引位在当前双向链表的前半段
if (index < (size >> 1)) {
//从当前双向链表的头节点开始向后依次查询
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//不成立,说明当前指定的index号索引位在双向链表的后半段,从当前双向链表的尾节点开始向后依次查询
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
LinkednList中,addAll(int,Collection)方法,get(int)方法,set(int,E)方法,add(int,E)方法,remove(int)方法等的读或写操作都使用node(int)方法查询双向链表中的指定的索引位
如indexOf(Object)方法,lastIndexOf(Object)方法,remove(Object)方法,则使用的数据对象引用信息来查询指定的索引位,以indexOf(Object)为例子,源码如下
public int indexOf(Object o) {
int index = 0;
//如果入参o为null
if (o == null) {
//从头节点开始向后遍历,直到找到某个item属性值为null的节点
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
//不为null,从头节点开始向后遍历,直到找到某个item属性值向的内存地址和传入参数o指向的内存地址相同的节点
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}