// 当前链表的元素个数
transient int size = 0;
// 链表的头部
transient Node<E> first;
// 链表的尾部
transient Node<E> last;
构造方法解析:
// 这种是创建一个空的链表
public LinkedList() {
}
// 使用已有的集合创建链表
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);//添加集合中所有元素到链表中
}
// List接口中的添加操作 :可以将元素放在头尾部;也可以添加到制定的索引处;也可以添加整个集合;
// add(E e)用于将元素添加到链表尾部,实现如下:
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++;
}
add(int index,E e)用于在指定位置添加元素。实现如下:
public void add(int index, E element) {
checkPositionIndex(index); //检索索引是否处于[0-size]之间
if (index == size)//如果相等,则添加在链表尾部
linkLast(element);
else //添加在链表中间
linkBefore(element, node(index));
}
在看linkBefore之前,先看一下node(int index)方法,该方法返回指定位置的节点,实现如下:
Node<E> node(int 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;
}
}
linkBefore()方法的解析:
void linkBefore(E e, Node<E> succ) {
// 创建newNode节点,将newNode的后继指针指向succ,前驱指针指向pred
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
// 将succ的前驱指针指向newNode
succ.prev = newNode;
//根据pred是否为null,进行不同操作。
// 如果为null,说明该节点插在头节点前面
if (pred == null)
first = newNode;
else
// 否则直接将pred的后继指针指向newNode即可
pred.next = newNode;
size++;
modCount++;
}
addAll方法解析:1. 检查index索引范围 2. 得到集合数据 3. 得到插入位置的前驱和后继节点 4. 遍历数据,将数据插入到指定位置
// 将集合从指定位置开始插入
public boolean addAll(int index, Collection<? extends E> c) {
// 检查index范围
checkPositionIndex(index);
// 得到传入集合的数据
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
// 得到插入位置的前驱节点和后继节点
Node<E> pred, succ;
// 如果插入位置为尾部,前驱节点为last,后继节点为null
if (index == size) {
succ = null;
pred = last;
}
// 否则,调用node()方法得到后继节点,再得到前驱节点
else {
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)
first = newNode;
else
// 否则,在尾部添加
pred.next = newNode;
pred = newNode;
}
// 如果插入位置在尾部,重置last节点
if (succ == null) {
last = pred;
}
// 否则,将插入的链表与先前链表连接起来
else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
Deque接口的添加操作
addFirst(E e)方法
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
final Node<E> f = first;
// 新建节点,以头节点为后继节点
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
// 如果链表为空,last节点也指向该节点
if (f == null)
last = newNode;
// 否则,将头节点的前驱指针指向新节点
else
f.prev = newNode;
size++;
modCount++;
}
总结:
- 将数据插入到链表尾部
- boolean add(E e):
- void addLast(E e)
- boolean offerLast(E e)
- 将数据插入到链表头部
- void addFirst(E e)
- boolean offerFirst(E e)
- 将数据插入到指定索引位置
- boolean add(int index,E e)