第一次读源码,可能有理解不对的地方,望指正。
0.LinkedList简介
LinkedList
是一个继承于AbstractSequentialList
的双向链表。它也可以被当做堆栈、队列或双端队列进行使用。LinkedList
实现List
接口,能让它进行队列操作。LinkedList
实现Deque
接口,即能将LinkedList
当做双端队列使用。LinkedList
实现Cloneable
,即覆盖了函数clone()
,能被克隆。LinkedList
实现了java.io.Serializable
接口,这意味着LinkedList
支持序列化,能通过序列化去传输。LinkedList
中的操作不是线程安全的。
1.构造方法
LinkedList一共有2个构造方法,下面我们看到的是一个无参构造方法,得到的是一个空链表。
public LinkedList() {
}
下面这个构造方法,首先会执行无参构造方法,然后将集合c的元素全部添加到空链表中。
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
从这个构造方法来看,LinkedList是一个无界链表,还记得ArrayList在它的源码实现中我们看到,每当执行add()方法的时候,都会检测数组边界以决定是否需要扩容。
2.添加元素
双向链表的头插法
private void linkFirst(E e) {
//将内部链表的first节点赋给f
final Node<E> f = first;
//newNode节点当前值为e,下一个节点是f,没有前驱结点
final Node<E> newNode = new Node<>(null, e, f);
//将新节点newNode当作first节点
first = newNode;
//判断是不是第一个添加的元素
//如果是第一个添加的元素,将新节点的值赋给last
//否则的话,将f的前驱节点设置为newNode新节点
if (f == null)
last = newNode;
else
f.prev = newNode;
//链表元素个数+1
size++;
//添加元素,modCount++
modCount++;
}
双向链表的尾插法
void linkLast(E e) {
//将last节点赋给l
final Node<E> l = last;
//新节点的元素值为e,前驱节点为l,后继节点为null
final Node<E> newNode = new Node<>(l, e, null);
//将新节点的值赋给last结点
last = newNode;
//如果原来的尾节点是null,说明这是第一次添加结点,那么就将新节点赋给first节点
//否则就让原来的尾节点的后继节点设为新节点
if (l == null)
first = newNode;
else
l.next = newNode;
//链表数量++
size++;
//被修改次数++
modCount++;
}
linkBefore(E e, Node succ)方法
//将元素插入到指定节点前
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
//首先获取指定结点的前驱结点
final Node<E> pred = succ.prev;
//将新节点的值设为e,前驱结点为pred,后继节点为succ
final Node<E> newNode = new Node<>(pred, e, succ);
//将新节点赋给指定结点的前驱结点
succ.prev = newNode;
//如果pred为空,那说明pred是个头节点,这是第一次插入结点
//所以将newNode结点赋给first结点
//若pred不为空,那么就让pred结点的后继结点为新的结点
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
add(E e)默认是添加到链表的尾部。
public void addFirst(E e) {
linkFirst(e);
}
public void addLast(E e) {
linkLast(e);
}
public boolean add(E e) {
linkLast(e);
return true;
}
//在指定位置添加元素
public void add(int index, E element) {
//检查是否满足index >= 0 && index <= size 的条件,不满足抛出异常
checkPositionIndex(index);
//如果插入的位置正好在链表的尾部直接尾插法
if (index == size)
linkLast(element);
else
//如果不是链表的尾部,那就在node(index)结点之前插入element元素
linkBefore(element, node(index));
}
public boolean addAll(Collection<? extends E> c) {
//返回的结果是addAll(int index, Collection<? extends E> c)的执行情况
return addAll(size, c);
}
addAll(int index, Collection<? extends E> c)方法。在下标为index的地方,插入集合c。
public boolean addAll(int index, Collection<? extends E> c) {
//检查是否满足index >= 0 && index <= size 的条件,不满足抛出异常
checkPositionIndex(index);
//将集合编程数组
Object[] a = c.toArray();
//获取数组a的长度
int numNew = a.length;
//如果数组为空,那么返回false
if (numNew == 0)
return false;
//定义2个结点,pred代表前一个结点,succ代表后一个结点
Node<E> pred, succ;
//index==size,那就直接尾部插入即可
//若index!=size,那就说明是在链表中的某个节点处插入
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//new一个结点,元素值为e,前驱结点为pred,后继结点为null
Node<E> newNode = new Node<>(pred, e, null);
//如果pred为null说明是pred为头节点
//则将新节点赋给first结点
//如果pred!=null
//就让pred的后继结点指向新节点
if (pred == null)
first = newNode;
else
pred.next = newNode;
//将newNode赋给pred
pred = newNode;
}
//如果是在链表尾部添加的,那么last结点应该为newNode结点(pred = newNode)
if (succ == null) {
last = pred;
} else {
//此时pred在新节点处,下面的操作是让pred结点和后面的结点进行双向连接
pred.next = succ;
succ.prev = pred;
}
//链表的个数更新
size += numNew;
//修改了链表,modCount++
modCount++;
//修改成功
return true;
}
3.删除元素
unlinkFirst(Node f)方法
//删除链表的第一个结点
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
//将被删除结点的后继结点赋给next
final Node<E> next = f.next;
//删除f结点
f.item = null;
f.next = null; // help GC
//令next结点赋给first结点
first = next;
//如果next结点为空,说明此时链表是个空的,理应让last为null
//否则,让next结点的前驱结点为null,因为next已经是第一个节点了
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
unlinkLast(Node l)方法
//删除链表最后一个结点
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
//将被删除结点的前驱结点赋给prev
final Node<E> prev = l.prev;
//删除l结点
l.item = null;
l.prev = null; // help GC
//将prev结点赋给last结点
last = prev;
//如果prev结点为null说明此时链表已经空了,理应让first结点为null
//否则让prev的后继节点为null,因为prev此时已经是最后一个节点了,后面应该是null
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
unlink(Node x)方法
//删除结点x
E unlink(Node<E> x) {
// assert x != null;
//获取删除结点的值
final E element = x.item;
//获取被删除结点的前驱结点和后继结点
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//如果prev为null,则被删除的结点为first结点,那么就将next结点赋给first结点
//使得next成为新的first结点
//否则的话,就让prev的后继节点指向next,x.prev为null
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
//如果next为null,说明被删除结点为last结点,那么让prev结点赋给last结点
//使得prev成为新的last结点
//否则的话,让next的前驱结点指向prev,x.next为null
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
remove(Object o)方法
//删除item为o的结点
public boolean remove(Object o) {
//分o==null和!=null两种情况,思路都是从头开始遍历链表,比较,调用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;
}
remove(int index)方法
//删除下标为index的结点
public E remove(int index) {
//判断index是否合法
checkElementIndex(index);
//返回unlink删除node(index)的结果
return unlink(node(index));
}
remove()方法
public E remove() {
//返回删除链表中第一个结点的结果
return removeFirst();
}
removeFirst()方法
//删除链表中第一个结点
public E removeFirst() {
final Node<E> f = first;
//如果首节点为null,说明是空链表,抛出异常
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
removeLast()方法
public E removeLast() {
final Node<E> l = last;
//空链表,没有元素可以删
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
clear()方法
//删除链表中的所有结点
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
//从first结点开始遍历,结点挨个删除
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
//first结点和last都为null
first = last = null;
//链表的结点数量为0
size = 0;
//修改链表结构次数++
modCount++;
}
4.获取元素
getFirst()方法
//返回first结点的值
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
getLast()方法
//返回last结点的值
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
get(int index)方法
//返回下标为index的节点值
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
5.更新元素
set(int index, E element)方法
public E set(int index, E element) {
//检查index是否合法
checkElementIndex(index);
//获取更新之前index处的结点
Node<E> x = node(index);
//保存旧值
E oldVal = x.item;
//更新值
x.item = element;
return oldVal;
}
6.关于队列
//获取队列的第一个元素,如果为null会返回null
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//获取队列的第一个元素,如果为null会抛出异常
public E element() {
return getFirst();
}
//获取队列的第一个元素并删除该结点,如果为null会返回null
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//删除队列的第一个元素,如果为null会抛出异常
public E remove() {
return removeFirst();
}
//将元素添加到队列尾部
public boolean offer(E e) {
return add(e);
}
7.关于双端队列
//将元素添加到首部
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
//将元素添加到尾部
public boolean offerLast(E e) {
addLast(e);
return true;
}
//获取首部的元素值
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//获取尾部的元素值
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
//删除首部,如果为null会返回null
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//删除尾部,如果为null会返回null
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
//将元素添加到首部
public void push(E e) {
addFirst(e);
}
//删除首部,如果为null会抛出异常
public E pop() {
return removeFirst();
}
//删除链表中元素值等于o的第一个节点,其实和remove方法是一样的,因为内部还是调用的remove方法
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
//删除链表中元素值等于o的最后一个节点
public boolean removeLastOccurrence(Object o) {
//因为LinkedList允许存在null,所以需要进行null判断
if (o == null) {
//和remove方法的区别它是从尾节点往前遍历
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
//调用unlink方法删除指定节点
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
8.其他方法
//判断元素是否存在于链表中
public boolean contains(Object o) {
return indexOf(o) != -1;
}
//从前往后查找返回节点元素值等于o的位置,不存在返回-1
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;
}
//该方法和上面indexOf方法相反,它是从后往前查找返回节点元素值等于o的位置,不存在返回-1
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;
}
//克隆函数,返回LinkedList的克隆对象
public Object clone() {
LinkedList<E> clone = superClone();
// 将新建LinkedList置于最初状态
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
// 将链表中所有节点的数据都添加到克隆对象中
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
//返回LinkedList节点单元素值的Object数组
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
//将链表中所有节点的元素值添加到object数组中
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
// 返回LinkedList的模板数组。所谓模板数组,即可以将T设为任意的数据类型
public <T> T[] toArray(T[] a) {
//如果a的长度小于LinkedList节点个数,说明a不能容纳LinkedList的所有节点元素值
//则新建一个数组,数组大小为LinkedList节点个数,并赋值给a
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
// 将链表中所有节点的元素值都添加到数组a中
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}
//将LinkedList中的数据写入到输入流中,先写容量,再写数据
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out size
s.writeInt(size);
// Write out all elements in the proper order.
for (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
//从输入流中读取数据,一样是先读容量,再读数据
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read in size
int size = s.readInt();
//从输入流中将元素值逐个添加到链表中
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
linkLast((E)s.readObject());
}
9.总结
- LinkedList自己实现了序列化和反序列化,因为它实现了writeObject和readObject方法。
- LinkedList是一个以双端链表实现的List,添加/删除元素只会影响周围的两个节点,开销很低。
- LinkedList还是一个双端队列,具有队列、双端队列、栈的特性。
- LinkedList在首部和尾部添加、删除元素效率高效,在中间添加、删除元素效率较低。
- LinkedList虽然实现了随机访问,但是只能顺序遍历,无法按照索引获得元素,效率低效,不建议使用。
- LinkedList是线程不安全的。
- 没有固定容量,不需要扩容,ArrayList添加元素就需要考虑扩容;
- 需要更多的内存,LinkedList 每个节点中需要多存储前后节点的信息,占用空间更多些。