更详细的讲解可以参考这篇博文。 面试必备:LinkedList源码解析(JDK8)
如果阅读过程中遇到了问题,可以返回来看看这篇。
//集合元素数量
transient int size = 0;
//链表头节点
transient Node<E> first;
//尾节点
transient Node<E> last;
//默认构造为空,什么也不做
public LinkedList() {
}
//将集合c所有元素插入链表中
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
//尾部批量插入元素
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c); //从下标为size的位置开始,插入集合c中所有元素
}
//Node节点结构
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;
}
}
可以看出是双向链表。
//在尾部插入一个元素
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++; //更新size
modCount++; //更新modCount
}
//在指定位置插入元素
public void add(int index, E element) {
checkPositionIndex(index); //越界判断
if (index == size)
linkLast(element); //在尾部插入
else
linkBefore(element, node(index)); //在中间插入
}
//在succ节点之前插入节点e
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev; //保存index节点的前驱节点
final Node<E> newNode = new Node<>(pred, e, succ);//创建新节点
succ.prev = newNode; //index节点的前驱节点指向新节点
if (pred == null) //index是头节点
first = newNode; //头节点指向新节点
else
pred.next = newNode; //index前驱节点的后置节点指向新节点
size++;
modCount++;
}
//从下标为的index的地方开始,插入集合c中所有元素
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//检查越界 [0,size] 闭区间
Object[] a = c.toArray(); //拿到目标集合数组
int numNew = a.length; //添加元素数量
if (numNew == 0) //如果添加元素数量为0,则不增加,并返回false
return false;
Node<E> pred, succ; //index的前驱与后置节点
if (index == size) { //在链表尾部追加数据
succ = null; //队尾后置节点一定是null
pred = last; //前置节点是尾节点
} else {
succ = node(index); //取出index节点,作为后置节点
pred = succ.prev; //前置节点是index的前置节点
}
//链表批量增加,是靠for循环遍历原数组,依次执行插入节点操作。
//对比ArrayList是通过System.arraycopy完成批量增加的
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; //更新前驱节点
}
if (succ == null) { //循环结束,如果后置节点为空,说明是尾节点
last = pred; //更新尾节点
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew; //更新size
modCount++; //更新modeCount
return true;
}
链表批量添加通过for循环遍历链表,将元素依次插入完成的。ArrayList则是通过System.arraycopy()完成的。
//根据指定对象删除
public boolean remove(Object o) {
//根据Object是否为null,分情况查找
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) { //x.item == null
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) { //o.equals(x.item)
unlink(x);
return true;
}
}
}
return false;
}
//根据指定索引删除
public E remove(int index) {
//检查是否越界 下标[0,size)
checkElementIndex(index);
return unlink(node(index));
}
//将节点从链表中删除
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) { //index的前驱节点为空,说明index是头节点
first = next; //头节点指向index的后置节点
} else {
prev.next = next; //index前驱节点的后置节点指向index的后置节点
x.prev = null; //index的前驱节点置空
}
if (next == null) { //index的后置节点为空,说明index是尾节点
last = prev; //尾节点指向为index的前驱节点
} else {
next.prev = prev; //index后置节点的前驱节点指向index的前驱节点
x.next = null; //index的后置节点置空
}
x.item = null; //index的元素值置空
size--;
modCount++;
return element;
}
public E set(int index, E element) {
checkElementIndex(index); //越界检查[0,size)
Node<E> x = node(index); //获取要修改的节点
E oldVal = x.item; //获取节点值
x.item = element; //更新节点值
return oldVal; //返回旧值
}
//获取指定位置的节点的value
public E get(int index) {
checkElementIndex(index);
return node(index).item; //先获取index节点,在返回节点值
}
//根据index 查询出Node,
Node<E> node(int index) {
// assert isElementIndex(index);
//通过下标获取某个node 的时候,(增、查),
//会根据index处于前半段还是后半段,进行一个折半,以提升查询效率
if (index < (size >> 1)) { //index在前半段
Node<E> x = first; //从头节点开始
for (int i = 0; i < index; i++)
x = x.next; //后置节点
return x;
} else { //index在后半段
Node<E> x = last; //从尾节点开始
for (int i = size - 1; i > index; i--)
x = x.prev; //前驱节点
return x;
}
}
可以看到LinkedList在查找元素时,将链表分为两份进行查找,虽然远不及ArrayList,但也可在一定程度上提高查找效率。
//LinkedList继承了AbstractSequentialList类,并且没有重写Iterator()
public Iterator<E> iterator() {
return listIterator(); //直接返回一个listIterator对象
}
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index); //直接返回一个ListItr对象,index是返回的第一个元素的索引(0)
}
//ListItr是实现了ListIterator接口
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned; //上一个的节点
private Node<E> next; //下一个的节点
private int nextIndex; //下一个节点的下标
private int expectedModCount = modCount;
//有参构造
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index); //next = 下标为index的节点
nextIndex = index; //下一个要处理的节点下标是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())// 判断是否存在 prev 节点
throw new NoSuchElementException();
//next为空的话,lastReturned与next都指向last,否则的话,next = next.prev
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--; //更新下一个要处理的节点下标
return lastReturned.item; //返回本次处理的节点的值
}
//返回下一个要处理的节点下标
public int nextIndex() {
return nextIndex;
}
//返回本次上一次处理的节点的下标
public int previousIndex() {
return nextIndex - 1;
}
// 删除链表当前节点也就是调用 next/previous 返回的这节点,也就 lastReturned
public void remove() {
//越界检查
checkForComodification();
//当前节点为空的话,抛异常
if (lastReturned == null)
throw new IllegalStateException();
//保存本节点的后置节点
Node<E> lastNext = lastReturned.next;
unlink(lastReturned); //删除本节点
// 调用过previous()
if (next == lastReturned)
next = lastNext; //更新next指向next.next
else
// 调用过next方法
nextIndex--; //更新下一个节点下标,保证顺序正确
lastReturned = null; //lastReturned节点置空
expectedModCount++; //更新expectedModCount
}
//更新
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e; //更新本节点的值
}
//在 next 节点位置插入及节点
public void add(E e) {
checkForComodification();// 检查 modCount 与 expectedModCount 是否相等
lastReturned = null;
if (next == null) //next节点为空,说明本节点是尾节点
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
注意,调用previous()会根据next是否为空,确定lastReturned与next的位置。将next指针向前移动一位。
//next为空的话,lastReturned与next都指向尾节点,否则的话,指向next.prev
lastReturned = next = (next == null) ? last : next.prev;
调用remove()也要判断迭代器是否调用过调用过previous()。
Node<E> lastNext = lastReturned.next;//保存当前节点的后置节点
unlink(lastReturned); //删除当前节点
// 说明remove之前调用过previous()
if (next == lastReturned)
next = lastNext; //next指向当前节点的后继节点,保证顺序的正确性
else
// 正常迭代
nextIndex--; //更新下一个节点下标,保证顺序正确
lastReturned = null; //lastReturned节点置空
原文:https://www.cnblogs.com/le-le/p/12399252.html