LinkedList 双向链表结构
总结
每个node都由前序节点node、当前元素值item和后续节点node组成
node=[prev_node,item,next_node]
注意这里是一个嵌套关系 即每个元素值都会存在3个地方
前一个节点都next属性中
当前节点的item属性中
后一个节点都prev属性中
下面分析源码组成部分
1. 列表首节点
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
2. 列表尾节点
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
3. 列表节点数量
transient int size = 0;
4. 静态内部类 Node
用来存放节点信息
Node 三部分组成
1. 后续节点next
2. 前序prev
3. 当前节点信息E
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;
}
}
5. 接下里来分析下方法
1.add方法实现
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
可以看到默认是添加到链表尾部Appends the specified element to the end of this list.
/**
* Links e as last element.
*/
void linkLast(E e) {
//最后一个节点赋值给变量l
final Node<E> l = last;
//初始化当前新增数据的节点 (前节点,当前节点元素,后续节点)
final Node<E> newNode = new Node<>(l, e, null);
// 当前节点是最后一个顾赋值给last
last = newNode;
//如果l == null 则说明当前列表为空列表 当前节点复制给首节点first
if (l == null)
first = newNode;
// 如果l!=null则说明当前列表已有节点 把新增前的尾节点last的next节点指向新增节点
else
l.next = newNode;
size++;
modCount++;
}
2.remove() 删除节点方法
首先猜测下删除节点方法的实现
假设数据列表有a b c三个连续节点 现在要remove(b)
分析:
把节点a的后续指针a.next指向c节点
把节点c的前序指针c.prev指向c节点
把b节点的值制空 GC
size减1
修改次数加1 迭代器用
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If this list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns {@code true} if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return {@code true} if this list contained the specified element
*/
public boolean remove(Object o) {
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(x)方法
/**
* Unlinks non-null node x.
*/
E unlink(Node<E> x) {
// assert x != null;
// 要删除的节点赋值给element
final E element = x.item;
// 要删除的节点的后续节点赋值给next
final Node<E> next = x.next;
// 要删除的节点的前序节点赋值给prev
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
// 前续节点的后续指针指向后续节点
prev.next = next;
// 要删除节点的前续指针清空
x.prev = null;
}
if (next == null) {
last = prev;
} else {
// 后续节点的前序指针指向前续节点
next.prev = prev;
// 要删除节点的后续指针清空
x.next = null;
}
// 要删除节点的当前节点清空
x.item = null;
// 列表的size减1
size--;
//修改次数加1
modCount++;
return element;
}
3.linkFirst添加首节点
只需要把首节点的前续指针指向新节点 新节点的后续指针指向当前首节点
/**
* Links e as first element.
*/
private void linkFirst(E e)
4.linkLast添加尾部节点
把尾节点的后续指针指向新节点
新节点的前续指针指向当前尾节点
/**
* Links e as last element.
*/
void linkLast(E e) {
5.linkBefore插入节点在某个节点前
/**
* Inserts element e before non-null Node succ.
*/
void linkBefore(E e, Node<E> succ)
6.unlinkFirst移除首节点/尾节点
/**
* Unlinks non-null first node f.
*/
private E unlinkFirst(Node<E> f) {
//内部私有方法 上层调用已经做判断 所以下面断言是一定生效的
// assert f == first && f != null;
...
}
/**
* Unlinks non-null last node l.
*/
private E unlinkLast(Node<E> l)
7.LinkedList 添加一个集合到LinkedList列表
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
这里其实就是循环♻️在当前列表添加一个个集合元素到队列尾部
自己觉得这块可以判断下类型 如果添加也是LinkedList可以做下判断
1、把添加列表的首节点和原来列表的尾节点建立关系
2、指定新的尾部节点
这样就不需要一个个元素循环了. 当然这样可能就不符合某些设计原则了
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element
* from the specified collection
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
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;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
8.peek检索 但不移除表头节点 没有返回null
public E peek()
9.element检索 但不移除表头节点
没有报错NoSuchElementException
public E element()
10.poll检索 且移除表头节点 没有返回null
public E poll()
11.remove 检索 且移除表头节点 没有报错NoSuchElementException
public E remove()
12.offer添加节点到列表尾部 返回true
public boolean offer(E e)
13.offerFirst在此列表的前面插入指定的元素。 返回true
public boolean offerFirst(E e)
14.offerLast在此列表的末尾插入指定的元素。返回true
public boolean offerLast(E e)
15.peekFirst检索但不删除此列表的第一个元素 返回列表的第一个元素或者返回null
public E peekFirst()
16.peekLast检索但不删除此列表的最后一个元素 返回列表的第后一个元素或者返回null
public E peekLast()
17.pollFirst检索并删除此列表的第一个元素 返回列表的第一个元素或者返回null
public E pollFirst()
18. pollLast检索并删除此列表的最后一个元素
返回列表的最后一个元素或者返回null
public E pollLast()
19.push将元素推送到此列表表示的堆栈上。
换句话说,在这个列表的前面插入元素。同addFirst
public void push(E e)
20.pop从此列表表示的堆栈中弹出一个元素。
换句话说,删除并返回此列表的第一个元素。同removeFirst
public E pop()
21.removeFirstOccurrence删除此列表中第一次出现的指定元素
(从头到尾遍历列表时)。如果列表不包含该元素,则它不变。
public boolean removeFirstOccurrence(Object o)
22.removeLastOccurrence删除此列表中指定元素的最后一次出现(从头到尾遍历列表时)。如果列表不包含该元素,则它不变。
public boolean removeLastOccurrence(Object o)
23.set将此列表中指定位置的元素替换为指定元素。没有抛IndexOutOfBoundsException
public E set(int index, E element)
24. remove移除此列表中指定位置的元素。将任何后续元素向左移动(从它们的索引中减去 1)。返回从列表中删除的元素。没有抛IndexOutOfBoundsException
public E remove(int index)
25.isElementIndex判断参数是否是现有元素的索引。
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
26.isPositionIndex迭代器或添加操作的时候判断参数是否是有效位置的索引。
private boolean isPositionIndex(int index){
return index >= 0 && index <= size;
}
27.checkElementIndex检查索引 没有抛IndexOutOfBoundsException
private void checkElementIndex(int index)
28.indexOf返回此列表中指定元素第一次出现的索引,
如果此列表不包含该元素,则返回 -1。更正式地说,返回最低索引 {@code i} 使得 (o==null ? get(i)==null : o.equals(get(i))),如果有,则返回 -1是没有这样的索引。
public int indexOf(Object o)
29.lastIndexOf返回此列表中指定元素最后一次出现的索引,
如果此列表不包含该元素,则返回 -1。更正式地说,返回最高索引 {@code i} 使得 (o==null ? get(i)==null : o.equals(get(i))),如果有,则返回 -1是没有这样的索引。
public int lastIndexOf(Object o)
30.listIterator返回此列表中元素的列表迭代器(以正确的顺序)迭代器
public ListIterator<E> listIterator()
31.clone返回此 {@code LinkedList} 的浅表副本。 (元素本身不会被克隆。)
public Object clone()
32.toArray以正确的顺序(从第一个元素到最后一个元素)返回包含此列表中所有元素的数组。 返回的数据是单独开辟的空间
public Object[] toArray()
33.toArray 以正确的顺序(从第一个元素到最后一个元素)返回一个包含此列表中所有元素的数组;
// 返回数组的运行时类型是指定数组的运行时类型。
// 如果列表适合指定的数组,则在其中返回。
// 否则,将使用指定数组的运行时类型和此列表的大小分配一个新数组
public <T> T[] toArray(T[] a)
34.writeObject重写了序列化和反序列化方法
//将此 {@code LinkedList} 实例的状态保存到流中(即序列化它)。
private void writeObject(java.io.ObjectOutputStream s)
35.readObject 从流中重构此 {@code LinkedList} 实例(即反序列化它)。
private void readObject(java.io.ObjectInputStream s)
6. 里面定义了三种迭代器,都是以内部类的方式实现,分别是:
DescendingIterator:倒序迭代器
一句话反着来就是了 别人正序我倒叙
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();
}
}
LLSpliterator:可分割迭代器
两个核心方法
迭代并执行给定的参数(参数=方法) forEachRemaining
分割 trySplit
⚠️这里的两个方法是有对应关系的 分割修改参数
这里有个知识点 java.util.function.Consumer 类的accept(T t); 对给定参数执行此操作。
也就是执行operation的方法作为参数传递 ,在需要的时候执行传递的方法
例如 list.spliterator().forEachRemaining(System.out::println); 就是传递了一个打印方法
Consumer<? super E> action
action.accept(e);
LinkedList的trySplit起始量尾1024
/** A customized variant of Spliterators.IteratorSpliterator */
static final class LLSpliterator<E> implements Spliterator<E> {
//BATCH_UNIT = 1024
static final int BATCH_UNIT = 1 << 10; // batch array size increment
//MAX_BATCH = xxxx一个比较大的数
static final int MAX_BATCH = 1 << 25; // max batch array size;
final LinkedList<E> list; // null OK unless traversed
Node<E> current; // current node; null until initialized
int est; // size estimate; -1 until first needed
int expectedModCount; // initialized when est set
int batch; // batch size for splits
LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
this.list = list;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getEst() {
int s; // force initialization
final LinkedList<E> lst;
if ((s = est) < 0) {
if ((lst = list) == null)
s = est = 0;
else {
expectedModCount = lst.modCount;
current = lst.first;
s = est = lst.size;
}
}
return s;
}
public long estimateSize() { return (long) getEst(); }
public Spliterator<E> trySplit() {
Node<E> p;
int s = getEst();
if (s > 1 && (p = current) != null) {
//这里达到1024切小于MAX_BATCH才会分隔
int n = batch + BATCH_UNIT;
if (n > s)
n = s;
if (n > MAX_BATCH)
n = MAX_BATCH;
Object[] a = new Object[n];
int j = 0;
do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
current = p;
batch = j;
//剩余量尾总量减去本次分割量
est = s - j;
return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
}
return null;
}
//循环执行给定的方法(正序)
public void forEachRemaining(Consumer<? super E> action) {
Node<E> p; int n;
if (action == null) throw new NullPointerException();
if ((n = getEst()) > 0 && (p = current) != null) {
current = null;
est = 0;
do {
E e = p.item;
p = p.next;
action.accept(e);
} while (p != null && --n > 0);
}
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
}
//单个执行给定的方法action
public boolean tryAdvance(Consumer<? super E> action) {
Node<E> p;
if (action == null) throw new NullPointerException();
if (getEst() > 0 && (p = current) != null) {
--est;
E e = p.item;
current = p.next;
action.accept(e);
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
ListItr:列表的经典迭代器
这个就是基础的迭代器Iterator的实现,方便列表进行便利
private int expectedModCount = modCount;
继承迭代器增加一个功能 -默认记录了修改次数,保障迭代过程的安全性
private class ListItr implements ListIterator<E> {
//上一次迭代返回的节点
private Node<E> lastReturned;
// lastReturned的正序下一个节点
private Node<E> next;
// lastReturned的正序下一个节点对索引
private int nextIndex;
//预期修改次数和实际修改次数
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(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();
}
}
【作者简介】:
里奥学JAVA,公众号【里奥学JAVA】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。这个世界希望一切都很快,更快,但是我希望自己能走好每一步,写好每一篇文章,期待和你们一起交流。
此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者核实删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~