一、前言
前面刚分析完ArrayList的源码,今天开始分析LinkedList的源码。本文将以
继承结构及实现的接口-> 涉及到的数据结构->类的属性->构造方法->常用的API 的顺序来阅读源码。
二、继承结构及实现的接口
LinkedList不同于ArrayList,ArrayList直接继承于AbstractList,而LinkedList先继承于AbstractSequentialList,然后间接继承于AbstractList。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
实现了List接口(集合)、Deque接口(支持两端元素插入和移除)、Cloneable接口(可拷贝)、Serializable接口(支持序列化)
三、数据结构
LinkedList的内部结构就是一个双端链表,有两个变量,一个first,一个last,分别指向该双端链表的头部和尾部。
结点组成
private static class Node<E> {
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;
}
}
四、类的属性
transient int size = 0;//当前链表中存储数据的个数
transient Node<E> first;//指向当前链表的头部
transient Node<E> last;//指向当前两边的尾部
protected transient int modCount = 0;//每链表结构发生改变时例如结点的增加、删除时modCount++,用来记录链表结构改变的次数
private static final long serialVersionUID = 876323262645176354L;//序列化Id 很大程度上避免反序列化过程的失败。比如当版本升级后,我们可能删除了某个成员变量,也可能增加了一些新的成员变量,这个时候我们的反序列化依然能够成功,程序依然能够最大程度地恢复数据
五、构造方法
默认的构造方法
public LinkedList() {
}//创建一个空的双向链表
用已有的集合来创建链表
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);//将集合c的所有元素添加到链表中
}
addAll方法
在LinkedList中addAll方法有两个重载方法
//将集合c中所有元素插入到链表的尾部,即size代表尾部位置
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//从指定位置来插入集合c的所有元素
public boolean addAll(int index, Collection<? extends E> c) {
//检查索引index是否合法
checkPositionIndex(index);
//将集合c转换成数组
Object[] a = c.toArray();
//得到集合c的元素数量
int numNew = a.length;
//如果待插入的集合为空,则返回false
if (numNew == 0)
return false;
//得到插入位置的前驱结点和后继结点
Node<E> pred, succ;
//如果插入位置是链表的尾部
if (index == size) {
succ = null;
pred = last;
} 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指向该结点
first = newNode;
else
//将新结点挂在前驱结点的后面
pred.next = newNode;
//更新前驱结点的位置
pred = newNode;
}
//判断刚才插入的位置是不是在链表尾部
if (succ == null) {
//last指向新的尾部
last = pred;
} else {
//如果刚才的插入位置是链表中的某处而不是尾部则将插入的链表和之前的链表链接起来
pred.next = succ;
succ.prev = pred;
}
//更新size,毕竟有新的结点插入了
size += numNew;
//结构改变计数器++
modCount++;
return true;
}
有两个参数的addAll方法步骤小结
- 检查待插入位置是否合法
- 将待插入集合转换成数组
- 得到插入位置的前驱、后继结点
- 遍历数组将其中的元素转换成结点后插入到指定位置
六、常用API
增
在链表尾部添加一个结点
offer方法()
public boolean offer(E e) {
return add(e);
}
add方法
public boolean add(E e) {
linkLast(e);
return true;
}
offerLast方法
public boolean offerLast(E e) {
addLast(e);
return true;
}
addLast方法
public void addLast(E e) {
linkLast(e);
}
所有向链表尾部添加结点的方法的底层实现
linkLast方法
void linkLast(E e) {
//获得链表的尾结点
final Node<E> l = last;
//创建待插入的结点
final Node<E> newNode = new Node<>(l, e, null);
//last指向新的尾结点
last = newNode;
//如果插入的结点是链表的第一个结点
if (l == null)
//将fisrt指向链表的第一个结点
first = newNode;
else
//在链表尾部挂上新结点
l.next = newNode;
//添加成功后更新size和modCount
size++;
modCount++;
}
linkLast方法执行步骤小结
- 获得尾结点
- 创建新结点
- 将新结点插入到尾结点的后面
在链表的头部插入一个结点
offerFirst方法
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
addFirst方法
public void addFirst(E e) {
linkFirst(e);
}
所有向链表头部添加结点的方法的底层实现
linkFirst方法
private void linkFirst(E e) {
//得到链表的头结点
final Node<E> f = first;
//创建新的结点
final Node<E> newNode = new Node<>(null, e, f);
//fisrt指向新的头结点
first = newNode;
//如果插入的新结点是该链表的第一个结点
if (f == null)
//意味着该结点也是链表的最后一个结点。将last指向该结点
last = newNode;
else
//更新旧的头结点的前驱结点
f.prev = newNode;
//成功插入结点后更新size 和modCount
size++;
modCount++;
}
linkFirst方法执行步骤小结
- 获得头结点
- 创建新结点
- 将新结点插入到头结点的前面
在指定位置添加元素
add(int index, E element)
public void add(int index, E element) {
//检查索引是否处于0~size之间
checkPositionIndex(index);
//如果插入的位置是尾部
if (index == size)
//调用linkLast,上面已经介绍过该方法
linkLast(element);
else
//添加元素到链表之中
linkBefore(element, node(index));
}
node方法
返回指定位置的结点
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方法
将待插入的结点插到指定位置的结点的前面(node(index)
void linkBefore(E e, Node<E> succ) {
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++;
}
删
删除指定值的结点
remove(Object o)方法
public boolean remove(Object o) {
//如果待删除的结点的值为空,则从前向后遍历链表,找到匹配的结点后,调用unlink方法进行删除
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;
}
unlink方法
从链表上删除给定的结点
E unlink(Node<E> x) {
//保存待删除结点的值
final E element = x.item;
//得到待删除结点的后继结点
final Node<E> next = x.next;
//得到待删除结点的前驱结点
final Node<E> prev = x.prev;
//如果删除的是链表的头结点,直接用first指向待删除结点的后继结点
if (prev == null) {
first = next;
} else {
//如果待删除结点不是头结点
//将待删除结点的前驱结点连到待删除结点的后继结点
prev.next = next;
//将待删除结点的前驱赋值为空
x.prev = null;
}
//如果待删除结点是链表的尾结点
if (next == null) {
//直接将last指向待删除结点的前驱结点
last = prev;
} else {
//待删除的结点不是尾结点也不是头结点
//将待删除结点的后继结点的前驱指向待删除结点的前驱结点
next.prev = prev;
//将待删除结点的后继赋值为空
x.next = null;
}
//将待删除结点的值赋值为空便于gc
x.item = null;
//更新size 和modCount
size--;
modCount++;
return element;
}
删除指定位置的结点
remove(int index)方法
public E remove(int index) {
//检查索引的合法性
checkElementIndex(index);
//删除指定位置的结点 unlink方法在上文已经详解
return unlink(node(index));
}
删除头结点
remove()方法
public E remove() {
//调用removeFirst方法删除头结点
return removeFirst();
}
pop()方法
public E pop() {
//调用removeFirst方法删除头结点
return removeFirst();
}
removeFirst()方法
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
//调用unlinkFirst删除头结点
return unlinkFirst(f);
}
poll()方法
public E poll() {
final Node<E> f = first;
//调用unlinkFirst删除头结点
return (f == null) ? null : unlinkFirst(f);
}
pollFirst() 方法
public E pollFirst() {
final Node<E> f = first;
//调用unlinkFirst删除头结点
return (f == null) ? null : unlinkFirst(f);
}
unlinkFirst(Node f)方法(删除头结点的底层实现)
private E unlinkFirst(Node<E> f) {
//保存待删除结点的值
final E element = f.item;
//得到待删除结点的后继结点
final Node<E> next = f.next;
//将待删除结点的值赋空,便于gc
f.item = null;
//将待删除结点的后继赋空,便于gc
f.next = null;
//用first指向待删除结点的后继结点
first = next;
//如果待删除结点是链表中唯一的结点
if (next == null)
//last赋空
last = null;
else
//否则跟新新的头结点的pre值
next.prev = null;
//更新size和modCount
size--;
modCount++;
//返回旧头结点的值
return element;
}
删除尾结点
removeLast()方法
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
//调用unlinkLast方法删除尾结点
return unlinkLast(l);
}
pollLast()方法
public E pollLast() {
final Node<E> l = last;
//调用unlinkLast方法删除尾结点
return (l == null) ? null : unlinkLast(l);
}
unlinkLast(Node l)方法(删除尾结点的底层实现)
private E unlinkLast(Node<E> l) {
//保存旧尾结点的值
final E element = l.item;
//获得旧尾结点的前驱结点
final Node<E> prev = l.prev;
//将旧尾结点的值赋空
l.item = null;
//将旧尾结点的前驱变量赋空
l.prev = null;
//将last指向旧尾结点的前驱结点
last = prev;
//如果待删除结点是链表中唯一的结点
if (prev == null)
//将first赋空
first = null;
else
//如果待删除结点不是链表中唯一的结点
//将新尾结点的next变量赋空
prev.next = null;
//更新szie modCount,在属性一栏有介绍
size--;
modCount++;
//返回旧尾结点的值
return element;
}
查
获得指定位置结点的值
get(int index)
public E get(int index) {
//检查索引的合法性即判断index的范围是否在 存在的链表的大小范围内
checkElementIndex(index);
//调用node方法(前面有详解)获得指定位置的结点,然后获得值
return node(index).item;
}
获得头结点的值
element()
public E element() {
return getFirst();
}
getFirst()
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
peekFirst()
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
peek()
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
获得尾结点的值
getLast()
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
peekLast()
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
获得指定值的结点在链表中的位置
indexOf(Object o)
public int indexOf(Object o) {
int index = 0;
//如果指定值为空,则从前向后遍历链表
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {//否则从后向前遍历
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
lastIndexOf(Object o) (返回和指定值相匹配中的结点中最后一个结点的位置)
public int lastIndexOf(Object o) {
int index = size;
//指定值为空,从后向前遍历
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else { //从后向前遍历
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
检查链表中是否存在和指定值向匹配的结点
contains(Object o)
public boolean contains(Object o) {
return indexOf(o) != -1;
}
改
修改指定位置的结点的值
set(int index, E element)
public E set(int index, E element) {
//检查索引的合法性是否越界
checkElementIndex(index);
//获得指定位置的结点
Node<E> x = node(index);
//保存该结点的旧值
E oldVal = x.item;
//给该结点赋新值
x.item = element;
//返回该结点的旧值
return oldVal;
}
迭代器(用来遍历链表)
获得一个迭代器,可以从指定位置进行迭代
listIterator(int index)
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
ListItr类
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
//得到当前索引指向的结点
next = (index == size) ? null : node(index);
//记录当前索引
nextIndex = index;
}
public boolean hasNext() {
return nextIndex < size;
}
//获得当前结点的下一个结点
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
public boolean hasPrevious() {
return nextIndex > 0;
}
//获得当前结点的前一个结点
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex - 1;
}
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
七、总结
总的来说,ArrayList和LinkedList的源码难度相比较HashMap低了很多,阅读起来基本没有什么难度。