一. 初始化
1.1 底层数据结构
1.1.1 数据结构
LinkedList底层数据结构是一个双端链表,每个节点不仅持有自己数据的值,还持有前节点和后节点的引用
1.1.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;
}
}
链表还维护着首尾节点的应用,方便进行首尾元素的增删
/**
* 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;
二. 添加元素
2.1 头部添加元素
模拟在链表头部添加元素。
- 首先把原先的头元素first记录下来,记为f
- 然后根据创建出来的节点新建出一个新的节点,这个新节点的next指向原先的头元素f。 此时状态如下
newNode
- first: null
- element: e
- next: f
- 把新节点赋值给原先的头部节点引用first(因为是头部插入)此时的状态如下
newNode/first
- first: null
- element: e
- next: f
- 判断f(即原先的头部节点)是不是空
- 如果为空,那么时在空链表中插入了一个元素,那么头部元素、尾部元素都时newNode
- 如果不为空,那么原先头部节点的前指针(因为已经时头部节点,所以前指针为空)需要指向新节点newNode,来完成节点的链接。
这个节点的链接,不能单方面的链接,必须两个节点双向链接才OK
/**
* Links e as first element.
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
2.2 尾部插入
实现原理和头部差不多
/**
* Links e as last element.
*/
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++;
}
2.3 插在某个节点之前
/**
* Inserts element e before non-null Node succ.
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}