概述
LinkedList实现了List接口,在之前使用ArrayList的地方可以替换成LinkedList直接使用。与ArrayList相比,LinkedList(没有实现RandomAccess接口)底层使用链表实现数据的存储,链表相对于数组,在查找获取方面不能直接通过索引获取,只能遍历获取,速度较慢。但相对于数组实现的ArrayList在插入和删除数据的时候,不存在容量扩容,也不需要数据的copy平移,速度上ArrayList快。
LinkedList 实现了Deque接口,Deque为一个双端队列接口,定义了队列,双端队列,栈操作的相关方法。也就是LinkedList可以用作队列,和栈容器。
类继承关系图:
LinkedList 实现了Iterable接口,说明该类可以通过迭代器遍历他。
LinkedList 实现了Collection接口,说明该类具有集合常规方法操作。
LinkedList 实现了List接口,说明该类具有线性表操作方法。
LinkedList实现了Queue/Deque接口,说明该类可以用作栈,队列,和双端队列。
LinkedList实现了Cloneable接口,说明该类具有clone方法。
LinkedList实现了Serializable接口,说明该类可以进行序列化。
成员变量:
//容器中元素个数
transient int size = 0;
//链表头部节点(指针)
transient Node<E> first;
//链表尾部节点(指针)
transient Node<E> last;
节点内部类:
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;
}
}
构造方法:
//默认构造方法,构造一个空的LinkedList
public LinkedList() {
}
//构造一个包含指定集合元素的LinkedList
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
List接口相关方法:
新增元素
//在链表尾部加入一个元素
public boolean add(E e) {
linkLast(e);
return true;
}
//在链表尾部加入一个node元素
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; //链表容量为空的时候加入一个元素 //first==last==newNode
else
l.next = newNode;//链表不为空,则直接链接到last元素后边
//容量加一
size++;
//修改modCount 用于迭代器遍历
modCount++;
}
//在链表指定接点加入一个node元素
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;//succ前节点为空,则将first节点赋值newNode
else
pred.next = newNode;//succ前节点不为空则直接将新加节点链接到前节点的后继节点
size++;//容量加一
modCount++;//修改modCount 用于迭代器遍历
}
//指定添加一个元素
public void add(int index, E element) {
//校验index是否越界
checkPositionIndex(index);
if (index == size)
linkLast(element);//index为最后一个元素,链接到链表尾部
else
linkBefore(element, node(index));//index不为最后一个元素,则将新元素链接到该指定位置
}
//校验索引是否越界,越界抛出异常
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//判断索引位置是否合法
boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
//添加指定集合中的元素到LinkedList
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//具体的添加指定集合元素方法
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//校验是否越界
//集合转化为数组
Object[] a = c.toArray();
int numNew = a.length;
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 = newNode;//从链表头部加入集合元素
else
pred.next = newNode;//依次加入新增元素
pred = newNode;//pred指向当前最新节点元素
}
if (succ == null) {//index插入位置为链表尾部则last指向最后一个新增元素节点
last = pred;
} else {//index插入位置为链表其他位置,则pred后继节点指向断开节点
pred.next = succ;
succ.prev = pred; 断开节点前继节点指向添加元素位置
}
size += numNew;//LinkedList数量增加numNew
modCount++;//修改modCount 用于迭代器遍历
return true;
}
删除元素
//删除指定元素
public boolean remove(Object o) {
//LinkedList允许元素为NULL
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;
}
//从链表中移除某个节点
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;
//处理移除节点的前继节点
if (prev == null) {
first = next;//移除节点为链表头部,则头节点直接指向next节点
} else {
prev.next = next;//移除节点不为链表头部,则移除节点的前继节点的后继节点指向next节点
x.prev = null;//将移除节点的前继节点置NULL
}
//处理移除节点的后继节点
if (next == null) {//移除节点为链表尾部,则将尾部节点直接指向移除节点的前继节点
last = prev;
} else {//移除节点不为链表尾部,则移除节点的后继节点的前继节点指向移除节点的后继节点
next.prev = prev;
x.next = null;//将移除节点的后继节点置NULL
}
//移除节点数据端置NULL便于垃圾回收
x.item = null;
size--;//数量减一
modCount++;//修改modCount值
return element;
}
//清空LinkedList
public void clear() {
//从头节点遍历置空节点
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;//头部指针和尾部指针置空
size = 0;//LinkedList大小置空
modCount++;//修改modCount值
}
查找元素
//获取指定位置元素
public E get(int index) {
//校验索引是否合法
checkElementIndex(index);
//调用node方法获取元素数据
return node(index).item;
}
//检验索引是否在合理范围,不合理抛出IndexOutOfBoundsException异常
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//真正校验索引范围
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
//获取指定索引位置节点方法
Node<E> node(int index) {
// assert isElementIndex(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;
}
}
修改元素
//修改指定位置元素
public E set(int index, E element) {
//校验索引位置是否合法
checkElementIndex(index);
//获取指定索引节点
Node<E> x = node(index);
//节点数据赋值
E oldVal = x.item;
x.item = element;
return oldVal;
}
迭代器
//获取指定范围的迭代器,且可以正向和反向迭代遍历
public ListIterator<E> listIterator(int index) {
//校验索引是否在合法范围
checkPositionIndex(index);
return new ListItr(index);
}
//新版本迭代器内部类
private class ListItr implements ListIterator<E> {
//迭代遍历返回的当前临时节点(next方法中使用,用于缓存当前节点),初始值为null,迭代器迭代遍历开始后指向容器当前节点值。
private Node<E> lastReturned;
//当前节点 next方法调用后指向下一个节点
private Node<E> next;
//当前节点的索引值,next方法调用后指向下一个节点索引值,用于判断是否还有数据
private int nextIndex;
//迭代器遍历时候判断容器是否发生过变化
private int expectedModCount = modCount;
//指定索引位置的构造方法
ListItr(int index) {
// assert isPositionIndex(index);
//index==size 则next为空,表示迭代器处于遍历结束状态,否则等于node方法返回值,node方法内部有index越界判断,越界则抛出异常
next = (index == size) ? null : node(index);
//当前节点索引值,next方法调用后指向下一个节点索引值
nextIndex = index;
}
//判断正向遍历是否还有元素
public boolean hasNext() {
return nextIndex < size;
}
//正向迭代遍历取值
public E next() {
//校验LinkedList是否修改过 若修改过抛出异常
checkForComodification();
//若正向遍历没有值则抛异常
if (!hasNext())
throw new NoSuchElementException();
//缓存当前节点引用,用于节点元素值返回
lastReturned = next;
//next节点指向下个节点
next = next.next;
//nextIndex索引指向下个节点索引
nextIndex++;
//返回当前节点数据元素
return lastReturned.item;
}
//是否可以进行前向迭代
public boolean hasPrevious() {
return nextIndex > 0;
}
//前向遍历取值
public E previous() {
//校验LinkedList是否修改过,若修改过抛出异常
checkForComodification();
//若前向遍历无值,抛出异常
if (!hasPrevious())
throw new NoSuchElementException();
//缓存当前节点引用,用于节点元素值返回。此外对next赋值next的前驱节点,若next==null,
//则赋值当前容器的尾部节点(本人分析代码逻辑不会赋值尾部节点)
lastReturned = next = (next == null) ? last : next.prev;
//nextIndex索引指向前向节点索引
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);
//处理迭代器状态(next==lastReturned逻辑,比较绕,发现调用next方法后,再调用previous方法,会出现此种逻辑,两者反向调用也会出现此种情况),
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;//移除当前节点后,lastRetunrned置空,垃圾回收
expectedModCount++;//修改容器状态变化标志,标识容器发生了变化
}
//通过迭代器更新元素
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
//直接赋值
lastReturned.item = e;
}
//通过迭代器新增元素
public void add(E e) {
//校验容器状态发生变化
checkForComodification();
//当前返回节点值置空
lastReturned = null;
//此处逻辑和LinkedList容器新增逻辑相同
if (next == null)
linkLast(e);//当前遍历器处于链表尾部,直接向链表尾部插入数据
else
linkBefore(e, next);//迭代器处于链表其他位置,则向指定节点前插入新节点
nextIndex++;//迭代器索引指向next所指向数据
expectedModCount++;//修改容器变化状态标志,标识容器发生了变化
}
//jdk1.8新加 jdk lambda表达式
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();
}
//校验LinkedList是否发生变化(新增元素,删除元素)
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
//获取老版本迭代器
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
//兼容1.6之前版本的老版本迭代器 该迭代器,就是对新迭代器的包装,只能进行正向遍历
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
其它方法
//链表转化为数组
public Object[] toArray() {
//new 一个新数组
Object[] result = new Object[size];
int i = 0;
//遍历将数据copy到数组
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
//序列化标志Id
private static final long serialVersionUID = 876323262645176354L;
//自定义序列化方法
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());
}
栈操作接口方法
//元素进栈方法
public void push(E e) {
addFirst(e);
}
//在链表头部加入新节点
public void addFirst(E e) {
linkFirst(e);
}
//真正链表头部加入节点方法
private void linkFirst(E e) {
final Node<E> f = first;
//构造新加入元素节点,新节点后继节点指向f节点,前继节点为null
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
//当容器为空的时候,last尾部节点==first==newNode
if (f == null)
last = newNode;
else
f.prev = newNode;//之前的头结点的前继节点指向新加入节点
size++;//修改容器当前数量
modCount++;//累加容器修改状态值
}
//元素出栈方法
public E pop() {
return 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;
//缓存节点元素
final E element = f.item;
//缓存当前节点
final Node<E> next = f.next;
//元素置空
f.item = null;
//后继节点置空
f.next = null; // help GC
//头节点指向next节点
first = next;
if (next == null)
last = null;//最后一个元素出栈,会走此逻辑
else
next.prev = null;//前继节点置空
//(first节点的前继节点都为空)
size--;//容量减一
modCount++;//累加,容器修改状态
return element;//返回出栈元素
}
队列操作接口方法
//向队列中加入元素
public boolean offer(E e) {
//内部调用add方法
return add(e);
}
//向队列加入元素此为list接口定义方法
public boolean add(E e) {
//在链表尾部加入一个元素
linkLast(e);
return true;
}
//在链表尾部加入一个元素
public boolean add(E e) {
linkLast(e);
return true;
}
//在链表尾部加入一个node元素
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; //链表容量为空的时候加入一个元素 //first==last==newNode
else
l.next = newNode;//链表不为空,则直接链接到last元素后边
//容量加一
size++;
//修改modCount 用于迭代器遍历
modCount++;
}
//从队列头部获取一个元素,并移除,当队列为空会抛出异常
public E remove() {
//内部调用removeFirst
return 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;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
//该函数和remove函数功能基本一致,只是当队列为空,返回null值
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//从队列中获取第一个元素,但不移除该元素,当队列为空返回null值
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//从队列中获取第一个元素,但不移除该元素,当队列为空抛出异常
public E element() {
return getFirst();
}
//真正的获取第一个元素
public E getFirst() {
final Node<E> f = first;
//队列会空抛出异常
if (f == null)
throw new NoSuchElementException();
return f.item;
}
双端队列接口方法
//向双端队列中头部加入一个元素
public void addFirst(E e) {
linkFirst(e);//链表头部加入节点
}
//向双端队列尾部加入一个元素
public void addLast(E e) {
linkLast(e);//链表尾部加入节点
}
//向双端队列中头部加入一个元素,和Addfirst方法一样只是加入了返回值
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
//向双端队列中头部加入一个元素,和addLast方法一样只是加入了返回值
public boolean offerLast(E e) {
addLast(e);
return true;
}
//从双端队列头部获取一个元素并移除该元素,当队列为空抛出异常
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//从双端队列尾部获取一个元素,并移除该元素,当队列为空抛出异常
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//从双端队列头部获取一个元素,并移除该元素,当队列为空返回null
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//从双端队列尾部获取一个元素,并移除该元素,当队列为空返回null
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
//从队列头部获取元素,不移除该元素,队列为空抛出异常
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
//从队列尾部获取元素,不移除该元素,队列为空抛出异常
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
//从队列头部获取元素,不移除该元素,队列为空返回NULL
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
//从队列尾部部获取元素,不移除该元素,队列为空返回NULL
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
从双端队列中移除某个元素,正向向遍历链表删除
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
//从双端队列中移除某个元素,反向遍历链表删除
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
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;
}
总结:
LinkedList底层由双向链表实现,可以轻松实现数据插入,和删除(比数组实现的ArrayList 效率高),不需要扩容,但查找访问效率比数组慢。
LinkedList实现了List接口,在使用ArrayList的地方可以替换使用。
LinkedList实现了Deque接口,可以用作队列,栈,双端队列。
LinkedList 不支持多线程,通过迭代器遍历的时候,如果有其他线程删除或新增元素,会抛出 ConcurrentModificationException()异常。
LinkedList 遍历删除元素,最好通过迭代器实现。LinkedList有两种迭代器,一种只能单向迭代,最新的一种可以双向,并且指定迭代范围。
LinkedList实现了自己的序列化方法。