前言:
上回咱们介绍了ArrayList源码,详情见《ArrayList源码,你想了解一下不?》文章,今天来介绍一下LinkedList源码,LinkedList源码也不是很难理解,它的底层用的双链表,如果你对常用的数据结构有所了解,你在学习理解LinkedList源码中关于链表的相关方法就很容易。LinkedList与ArrayList都实现了List接口,如果你原来看过ArrayList源码,你在学习理解LinkedList源码中关于List接口相关方法就很容易。听我这么半天忽悠,你是不是也想学习LinkedList源码。
零、LinkedList的理解:
LinkedList的功能比较强大,它实现了List和Deque两个接口的功能,在使用Deque双向队列功能时,它能模拟队列和栈两种数据结构。它的底层是使用的链表,链表这种数据结构在插入和删除元素时,时间复杂度要优于顺序表;在查询元素时,时间复杂度高于顺序表,但是LinkedList源码也做相关优化,它首先会比较索引位置值与一半size值的大小,小于从frist结点(头结点)遍历,反之从last结点(尾结点)遍历。
一、LinkedList的数据结构:
LinkedList的数据结构是一个双向链表,其中,这里有一个结点的概念,双向列表结点包含前继结点、元素、后继结点三个部分组成;这里也有size的概念,代表是链表中结点的个数:
二、LinkedList源码分析:
1.成员变量:
/**
* 链表中结点数量
*
*/
transient int size = 0;
/**
* 指向第一个结点的指针(头结点)
* 不变的: (first == null && last == null) || (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* 指向最后一个结点的指针(尾结点)
* 不变的: (first == null && last == null) ||(last.next == null && last.item != null)
*/
transient Node<E> last;
2.构造函数:
/**
* 构造函数
*/
public MyLinkedList() {
}
/**
* 构造函数,包含指定的集合,按集合返回它们的顺序迭代器。
*
* @param c 要将其元素放入此列表中的集合
* @throws NullPointerException 如果指定的集合为空
*/
public MyLinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
3.结点的数据结构方法:
/**
* 结点数据结构
*
* @param <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;
}
}
4.操作链表的相关方法:
(1)E linkFirst(E e) 方法:
/**
* Links e 作为第一个元素(头结点)
*
* @param e
*/
private void linkFirst(E e) {
// 把头结点赋值给 f
final Node<E> f = first;
// 创建一个新结点(前继:null,后继:f)
final Node<E> newNode = new Node<>(null, e, f);
// 把newNode赋值给first(头结点)
first = newNode;
// 如果f等于空,即原头结点为空,又因这个方法只是设置第一个元素(头结点),
// 所以说明链表为空
if (f == null) {
// 尾结点也是新结点(newNode),因为就一个结点
last = newNode;
} else {// 如果f不等于空,即原头结点不为空
// 原头结点的前继结点指向新结点(newNode)
f.prev = newNode;
}
// 结点数量加一
size++;
// modCount自增一
modCount++;
}
操作链表如下图:
情况1:头结点为空时,插入newNode结点:
情况2:头结点不为空时,插入newNode结点:
Tips:linkLast(E e)方法与 linkFirst(E e)类似,我就不展示了
(2)E linkBefore(E e, Node<E> succ)方法:
/**
* 将e插入succ结点前面
*
* @param e
* @param succ
*/
void linkBefore(E e, Node<E> succ) {
// 前提:assert succ != null;
// 获取succ结点的前继结点,赋值给pred
final Node<E> pred = succ.prev;
// 创建一个新结点(前继:pred,后继:succ)
final Node<E> newNode = new Node<>(pred, e, succ);
// 把newNode赋值为succ结点的前继结点
succ.prev = newNode;
// pred为空,证明该结点为头结点
if (pred == null) {
// 把newNode设置成头结点
first = newNode;
}else {// pred不为空
// 把newNode赋值为pred的后继结点
pred.next = newNode;
}
// 结点数量加一
size++;
// modCount自增一
modCount++;
}
操作链表如下图:
情况1:在A结点前,插入G结点:
情况2:在B结点前,插入G结点:
(3)E unlinkFirst(Node<E> f)方法:
/**
* 移除第一个结点f(头结点)
*
* @param f
* @return
*/
private E unlinkFirst(Node<E> f) {
// 前提: assert f == first && f != null;
// 获取结点f中的元素
final E element = f.item;
// 获取结点f的后继结点
final Node<E> next = f.next;
// 把结点f中的元素设置为空
f.item = null;
// 把结点f后继结点设置为空,help GC(帮助GC)
f.next = null;
// 把next设置为头结点
first = next;
// next结点为空,则证明链表中只有一个结点,即只有头结点一个
if (next == null) {
// 把last设置为空
last = null;
}else { //next结点不为空
// 把next的前继设置为空,即后继结点就变成头结点了
next.prev = null;
}
// 结点数量减一
size--;
// modCount自增一
modCount++;
// 返回移除的元素
return element;
}
操作链表如下图:
Tips:unlinkLast(Node<E> l)方法与 unlinkFirst(Node<E> f)类似,我就不展示了
(4)E unlink(Node<E> x)方法:
/**
* 移除链表上的x结点
*
* @param x
* @return
*/
E unlink(Node<E> x) {
// 前提: assert x != null;
// 获取结点x中的元素
final E element = x.item;
// 获取结点x的后继结点
final Node<E> next = x.next;
// 获取结点x的前继结点
final Node<E> prev = x.prev;
// 前继结点为空,则证明该结点为头结点
if (prev == null) {
// 把后继结点设置为头结点
first = next;
} else { // 前继结点不为空
// 把前继结点的后继设置为next
prev.next = next;
// 把x结点的前继设置为null
x.prev = null;
}
// 后继结点为空,则证明该结点为尾结点
if (next == null) {
// 把前继结点设置为尾结点
last = prev;
} else { // 后继结点不为空
// 把后继结点的前继设置为prev
next.prev = prev;
// 把x结点的后继设置为null
x.next = null;
}
// 把结点x中的元素设置为空
x.item = null;
// 结点数量减一
size--;
// modCount自增一
modCount++;
// 返回移除的元素
return element;
}
操作链表如下图:
5.List接口相关方法:
(1) E get( int index)方法:
/**
* 返回此列表中指定位置的元素
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@Override
public E get(int index) {
// 查询索引的范围
checkElementIndex(index);
// 返回元素
return node(index).item;
}
步骤:1.查询索引范围;2.返回元素。
checkElementIndex(int index)方法:
/**
* 检查元素索引
* @param index
*/
private void checkElementIndex(int index) {
if (!isElementIndex(index)) {
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
isElementIndex(int index)方法:
/**
* 判断元素的索引范围
*
* @param index
* @return
*/
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
node(int index)方法:
/**
* 返回指定元素索引处的(非空)结点。
*
* @param index
* @return
*/
Node<E> node(int index) {
// 前提:assert isElementIndex(index);
// index值小于一半的size时,从头结点遍历
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
} else { // index值大于一半的size时,从尾结点遍历
Node<E> x = last;
for (int i = size - 1; i > index; i--) {
x = x.prev;
}
return x;
}
}
Tips:这里遍历元素就进行一个优化。
(2) boolean add(E e)方法:
/**
* 将指定的元素追加到此列表的末尾
*
* 此方法相当于{@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
@Override
public boolean add(E e) {
linkLast(e);
return true;
}
步骤:1.添加一个尾结点元素;2.返回 true。
(3)boolean remove()方法:
/**
* 从列表中删除指定元素的第一个匹配项(如果存在)。如果此列表不包含元素,则它将保持不变。
* 更正式地说,删除索引{@code i}最低的元素。
* 这样 o==null?get(i)==null:o.equals(get(i)),如果存在这样的元素,如果此列表返回true,
* 包含指定的元素(如果此列表由于调用而更改)。
*
* @param o element to be removed from this list, if present
* @return {@code true} if this list contained the specified element
*/
@Override
public boolean remove(Object o) {
// 对象为空
if (o == null) {
// 循环遍历
for (Node<E> x = first; x != null; x = x.next) {
// 删除元素
if (x.item == null) {
// 移除链表上的x结点
unlink(x);
return true;
}
}
} else {
// 循环遍历
for (Node<E> x = first; x != null; x = x.next) {
// 删除元素
if (o.equals(x.item)) {
// 移除链表上的x结点
unlink(x);
return true;
}
}
}
// 不存在元素 false
return false;
}
步骤:1.判断对象是否为空;2.为空遍历时,使用==判断是否相等,再删除;不为空遍历时,使用equals判断是否相等,再删除。
Tips:这里只是删除索引最低的元素。
(4)void clear()方法:
/**
* 从列表中删除所有元素
* 此调用返回后,列表将为空
*
*/
@Override
public void clear() {
// 清除结点之间的所有链接是“不必要的”,但是:
//-如果丢弃的结点驻留在多个生成中,则帮助生成GC ??
//-即使存在可访问的迭代器,也一定要释放内存 ??
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;
modCount++;
}
步骤:循环遍历把所有节点的前继结点、元素、后继结点都设置为空,然后被GC回收。
6.Queue接口相关方法:
(1) E peek()方法:
/**
* 检索但不删除此列表的头(第一个元素),
* 有返回元素,无返回null
*
* @return 此列表的头,如果此列表为空,则为null
* @since 1.5
*/
@Override
public E peek() {
// 获取头结点
final Node<E> f = first;
// 获取头结点的元素
return (f == null) ? null : f.item;
}
步骤:1.获取头结点;2.获取并返回头结点的元素
(2)E element() 方法:
/**
* 检索但不删除此列表的头(第一个元素)。
* 有返回元素,无抛NoSuchElementException
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
*/
@Override
public E element() {
// 返回此列表中的第一个元素
return getFirst();
}
(3) E poll() 方法:
/**
* 检索并删除此列表的头(第一个元素)
* 有删除元素,无返回null
*
* @return the head of this list, or {@code null} if this list is empty
* @since 1.5
*/
@Override
public E poll() {
// 获取头结点
final Node<E> f = first;
// 删除头结点
return (f == null) ? null : unlinkFirst(f);
}
步骤:1.获取头结点;2.删除结点并返回删除结果
(4) E remove() 方法:
/**
* 检索并删除此列表的头(第一个元素)
* 有删除元素,无抛NoSuchElementException
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
*/
@Override
public E remove() {
// 从列表中删除并返回第一个元素
return removeFirst();
}
(5)boolean offer(E e) 方法:
/**
* 将指定元素添加为此列表的尾部(最后一个元素)
*
* @param e the element to add
* @return {@code true} (as specified by {@link Queue#offer})
* @since 1.5
*/
@Override
public boolean offer(E e) {
return add(e);
}
7.Deque接口相关方法:
(1)boolean offerFirst(E e)方法:
/**
* 在列表的前面插入指定的元素
*
* @param e the element to insert
* @return {@code true} (as specified by {@link Deque#offerFirst})
* @since 1.6
*/
@Override
public boolean offerFirst(E e) {
// 在列表的开头插入指定的元素
addFirst(e);
// 返回true
return true;
}
步骤1.在列表的开头插入指定的元素;2.返回 true。
(2)boolean offerLast(E e)方法:
/**
* 在列表末尾插入指定元素
*
* @param e the element to insert
* @return {@code true} (as specified by {@link Deque#offerLast})
* @since 1.6
*/
@Override
public boolean offerLast(E e) {
// 将指定的元素追加到此列表的末尾
addLast(e);
// 返回true
return true;
}
步骤1.将指定的元素追加到此列表的末尾;2.返回 true。
(3) E peekFirst() 方法:
/**
* 检索但不删除此列表的第一个元素,如果此列表为空,则返回{@code null}。
*
* @return the first element of this list, or {@code null}
* if this list is empty
* @since 1.6
*/
@Override
public E peekFirst() {
// 获取头结点
final Node<E> f = first;
// 返回元素
return (f == null) ? null : f.item;
}
步骤1.获取头结点;2.返回元素。
(4)E peekLast() 方法:
/**
* 检索但不删除此列表的最后一个元素,如果此列表为空,则返回{@code null}。
*
* @return the last element of this list, or {@code null}
* if this list is empty
* @since 1.6
*/
@Override
public E peekLast() {
// 获取尾结点
final Node<E> l = last;
// 返回元素
return (l == null) ? null : l.item;
}
步骤1.获取尾结点;2.返回元素。
(5)E pollFirst() 方法:
/**
* 检索并删除此列表的第一个元素,如果此列表为空,则返回{@code null}。
*
* @return the first element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
@Override
public E pollFirst() {
// 获取头结点
final Node<E> f = first;
// 删除元素
return (f == null) ? null : unlinkFirst(f);
}
步骤1.获取头结点;2.删除元素并返回结果。
(6) E pollLast() 方法:
/**
* 检索并删除此列表的最后一个元素,如果此列表为空,则返回null
*
* @return the last element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
@Override
public E pollLast() {
// 获取尾结点
final Node<E> l = last;
// 删除元素
return (l == null) ? null : unlinkLast(l);
}
步骤1.获取尾结点;2.删除元素并返回结果。
(7)void push(E e)方法:
/**
* 将元素推送到由该列表,表示的堆栈上。换句话说,在列表的前面插入元素。
*
* <p>This method is equivalent to {@link #addFirst}.
*
* @param e the element to push
* @since 1.6
*/
@Override
public void push(E e) {
// 在列表的开头插入指定的元素
addFirst(e);
}
(8)E pop()方法:
/**
* 从该列表表示的堆栈中弹出一个元素。换句话说,删除并返回此列表的第一个元素。
*
* 这种方法相当于 {@link #removeFirst()}.
*
* @return the element at the front of this list (which is the top
* of the stack represented by this list)
* @throws NoSuchElementException if this list is empty
* @since 1.6
*/
@Override
public E pop() {
// 从列表中删除并返回第一个元素
return removeFirst();
}
三、MyArrayList类源码:
springboot_demo: springboot_demo 《 springboot-java-basic-note模块-sourcecode.list.linkedlist包》
四、参考文件:
JDK1.8 LinkedList类源码