参考:
https://blog.youkuaiyun.com/mq2553299/article/details/76551221
https://www.cnblogs.com/dsxie/p/12945384.html
https://www.cnblogs.com/gsm340/p/9243916.html(关于LinkedList遍历)
https://blog.youkuaiyun.com/sujin_/article/details/81588413(用LinkedList实现栈和队列)
Java Collection库中有三类:List,Queue,Set;而List接口,有三个子实现类:ArrayList,Vector,LinkedList。
LinkedList如其名,链表,像锁链一样一环扣一环的数据结构。
我们先回顾一下链表的特性,和数组做一个对比
数组因为有下标,可以直接通过下标获取数据,所以查询效率很高,但是相应的,也是因为下标,在增删元素的时候,整体接口需要发生变化,代价比较大,比如我从数组中间删了一个元素,则该元素之后的元素下标都要发生变化,全部减一
链表没有下标,其结构特征是每一环,包含上一环的引用,像是锁链一样扣在一起,没有下标导致链表查询时只能从头向尾或者从尾向头挨个节点的遍历,这样做无疑效率低下,但是相应的,因为这种当前节点总包含前后节点引用的特性,我们只需改变节点中的引用信息就可以实现增删,这相对数组来说代价就小多了,不必变动其它部分的结构,假如我要删除某节点,只需要将它的前置节点中的对后的引用指向被删除节点的下一个节点,增加节点的话也是类似的,所以有了链表增删快,查询慢的特点
读源码之前,我们还是带着问题出发:
LinkedList是怎么实现链表结构来达到增删快 查询慢的特性的?LinkedList能帮我们实现什么样的数据结构?在什么样的场景下我们选择它来存储数据呢?LinkList是否是像ArrayList一样线程不安全的呢
1 LinkedList结构
//实现Cloneable,可以调用Object.clone()方法
//实现序列化接口,可以实现将list序列化成二进制数据和反序列化操作
//AbstractSequentialList抽象类,AbstractList的子类,继承
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
//Deque接口,继承自queue,queue是队列,典型的先进先进先出FIFO数据结构
//Deque既可以FIFO也可以LIFO这意味着实现这个接口的类既可以实现队列接口,也可以实现栈结构
public interface Deque<E> extends Queue<E>
/**
* This class provides a skeletal implementation of the <tt>List</tt>
* interface to minimize the effort required to implement this interface
* backed by a "sequential access" data store (such as a linked list). For
* random access data (such as an array), <tt>AbstractList</tt> should be used
* in preference to this class.<p>
*
//从注释中不难看出,继承此类,需要LinkedList只提供顺序访问
//如果需要随机访问,像数组,不能继承该类(in preference to this class)
public abstract class AbstractSequentialList<E> extends AbstractList<E>
2 属性
//链表元素个数
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
//头节点,首尾节点都是null(链表为null),或者首节点的前引用为null且后引用不为null
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
//尾节点,首尾节点都是null(链表为null),或者尾节点的后引用为null且前引用不为null
transient Node<E> last;
这里解释一下注释内容,对于头节点和尾巴节点,作为锁链中的一环,头节点自然是没有上一节点的,尾节点自然是没有下一节点的。
3 构造方法
/**
* Constructs an empty list.
*/
public LinkedList() {
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
//调用无参构造,其实就是个空方法
this();
//添加全部元素至LinkedList
addAll(c);
}
//addAll方法,此时对于新建的LinkedList实例,size=0
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/**
* 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);
//将Collection入参转换为数组
Object[] a = c.toArray();
//检查入参中是否包含元素,如果返回false,说明有参构造的Collection参数不包含元素
int numNew = a.length;
if (numNew == 0)
return false;
//链表节点Node
Node<E> pred, succ;
//验证index是否==size,如果等于,则在链表last元素后追加新元素
if (index == size) {
succ = null;
pred = last;
} else {
//如不等于,则获取下标为index元素,
succ = node(index);
pred = succ.prev;
}
//循环入参中的元素
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//新建一个节点,不包含next下一节点
Node<E> newNode = new Node<>(pred, e, null);
如果新建节点next==null,说明是头节点
if (pred == null)
first = newNode;
else
//否则,将前节点的next引用指向新建节点
pred.next = newNode;
pred = newNode;
}
//循环结束,如果传入index为链表长度,最后一次循环得到pred就是尾节点
if (succ == null) {
last = pred;
} else {
//如果index不等于size,说明是从中间插入的,最后一个元素也有下一节点
//该下一节点就是之前下标为index的元素
pred.next = succ;
succ.prev = pred;
}
//链表元素数量增加
size += numNew;
//迭代器需要用到的修改次数,若不为0,使用迭代器遍历会报错
modCount++;
return true;
}
//越界检查
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//越界检查
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
//链表中节点,包含自身引用及前后节点引用
//这样,链表中的节点便是包含前一节点和后一节点引用(除去首尾节点)
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;
}
}
/**
* Returns the (non-null) Node at the specified element index.
* 返回指定位置的非空节点
* 节点如果为空
*/
Node<E> node(int index) {
// assert isElementIndex(index);
//index大于size的一半,则从链表头部开始向下遍历处理
if (index < (size >> 1)) {
Node<E> x = first;
//从头遍历,找到下标为index的元素(同数组的下标)
//也就是说秒如果index=4,得到的,是第五个元素
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//否则,则从链表尾部开始向上遍历处理
Node<E> x = last;
//从尾巴节点的前节点开始找,假如size=8.index = 6.实际得到的,相当于数组中下标为7的元素
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
构造方法一是个无参构造,什么都没做
构造二接收一个Collection类型,并调用无参构造,在这之后调用addAll方法,同List Set一样,这个方法的作用,就是将Collection中的元素全部添加至当前Collection子类的实例容器中
构造二略复杂,它调用的addAll有两个用途,如果是通过构造方法中的调用去使用这个方法,作用就是从链表尾部开始追加Collection c中的元素,如果是我们自己调用addAll,且指定了size,就是从size处开始追加相当于在链表中间插入了一段数据
这个方法很灵活,在构造方法中也有使用,所以,在介绍构造时候也顺便介绍一下
4 常见方法
4.1 getFirst getLast addFirst addLast
/**
* Returns the first element in this list.
*
* @return the first element in this list
* @throws NoSuchElementException if this list is empty
*/
//获取首节点中元素
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* Returns the last element in this list.
*
* @return the last element in this list
* @throws NoSuchElementException if this list is empty
*/
//获取尾节点中元素
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
//添加首节点
public void addFirst(E e) {
linkFirst(e);
}
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #add}.
*
* @param e the element to add
*/
//添加尾节点
public void addLast(E e) {
linkLast(e);
}
/**
* Links e as first element.
* 将e指定为链表的首节点
*/
private void linkFirst(E e) {
//使用引用f指向原来的首节点
final Node<E> f = first;
//新建节点对象,本节点为e,下一节点为f
final Node<E> newNode = new Node<>(null, e, f);
//将新建节点置为首节点
first = newNode;
//原首节点为空,首尾节点都为空(链表无元素)或者,首节点prev为空,且自身不为空(有元素)
//让newNode为尾节点,无论e是否为空,都至少满足上面条件之一
//e不为null,则实际上称为首节点,e为null,则链表是空的
if (f == null)
last = newNode;
else
//否则,首节点的prev引用指向newNode
f.prev = newNode;
size++;
modCount++;
}
/**
* Links e as last element.
* 在链表尾部加元素,参考上面方法
*/
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++;
modCount++;
}
/**
* Inserts element e before non-null Node succ.
* 在一个非空节点前插入一个元素
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
//要操作的节点,succ,将succ的前节点换成e的新建节点newNode
//再将该前节点的下一节点指向newNode,再将succ的prev赋值为newNode
//中间加一个是否是头节点的判断(既然是指定位置插入,那至少是尾节点之前的插入位置)
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
4.2 removeFirst removeLast add remove
/**
* Removes and returns the first element from this list.
*
* @return the first element from this list
* @throws NoSuchElementException if this list is empty
*/
//移除首节点中元素
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
/**
* Removes and returns the last element from this list.
*
* @return the last element from this list
* @throws NoSuchElementException if this list is empty
*/
//移除尾节点中元素
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 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;
}
/**
* 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) {
//移除,从天遍历,移除为null节点
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
//非null,移除符合条件节点
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
/**
* Unlinks non-null first node f.
* 移除首节点
*/
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
//将首节点及首节点next引用置为null
f.item = null;
f.next = null; // help GC
first = next;
//首节点next引用为null,说明链表只有一个元素
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
/**
* Unlinks non-null last node l.
* 移除尾节点
*/
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
/**
* Unlinks non-null node 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;
//若x节点前置节点为null,x节点为首节点
//将x节点下一节点设置为首节点(移除当前节点)
if (prev == null) {
first = next;
} else {
//x节点prev非null,将x前节点的next置为x节点的next
prev.next = next;
//GC
x.prev = null;
}
if (next == null) {
//与上面相反,为尾巴节点
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
4.3 get clear set
//移除链表中所有元素
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
从头不遍历,将所有元素置为null
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++;
}
// Positional Access Operations
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
//获取下标为index的元素
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
//先获取指定位置节点,再将节点item设置为element
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
4.4 peek element
/**
* Retrieves, but does not remove, the head (first element) of this list.
*
* @return the head of this list, or {@code null} if this list is empty
* @since 1.5
* 获得首节点内元素值,(不删除首节点)链表为空不抛异常
*/
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* Retrieves, but does not remove, the head (first element) of this list.
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
* 获得首节点内元素值,(不删除首节点)链表为空抛异常
*/
public E element() {
return getFirst();
}
这两个方法的区别就是再链表为空(不包含元素)的时候是否抛出异常,所以具体应用场景不太一样
4.5 poll remove
/**
* Retrieves and removes the head (first element) of this list.
*
* @return the head of this list, or {@code null} if this list is empty
* @since 1.5
* 这两个方法都会获取并删除首节点,同样也是一个抛异常一个不抛
*/
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
* Retrieves and removes the head (first element) of this list.
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
*/
public E remove() {
return removeFirst();
}
4.6 offer offerFirst offerLast
public boolean offer(E e) {
return add(e);
}
// Deque operations
/**
* Inserts the specified element at the front of this list.
*
* @param e the element to insert
* @return {@code true} (as specified by {@link Deque#offerFirst})
* @since 1.6
*/
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
/**
* Inserts the specified element at the end of this list.
*
* @param e the element to insert
* @return {@code true} (as specified by {@link Deque#offerLast})
* @since 1.6
*/
public boolean offerLast(E e) {
addLast(e);
return true;
}
offer 和offer是在链表尾部插入元素,offerFirst是在链表首部插入元素,方法前面都分析过,就不赘述了
4.7 pollFirst pollLast push pop
/**
* Retrieves and removes the first element of this list,
* or returns {@code null} if this list is empty.
*
* @return the first element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
* Retrieves and removes the last element of this list,
* or returns {@code null} if this list is empty.
*
* @return the last element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
/**
* Pushes an element onto the stack represented by this list. In other
* words, inserts the element at the front of this list.
*
* <p>This method is equivalent to {@link #addFirst}.
*
* @param e the element to push
* @since 1.6
*/
public void push(E e) {
addFirst(e);
}
/**
* Pops an element from the stack represented by this list. In other
* words, removes and returns the first element of this list.
*
* <p>This method is equivalent to {@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
*/
public E pop() {
return removeFirst();
}
小结:
没有任何控制线程安全的操作,所以LinkedList是线程不安全的
由于是链表的数据结构,具有增删快,但是查询慢的特性
利用push和pop可以实现先进后出的栈数据结构,利用offerFirst和pullLast可以实现先进先出的队列数据结构,具体使用场景根据需要才不用不同方式(概括就是,LinkedList可以实现栈,也可以实现队列的数据结构)
由于遍历链表时取数据总是从头尾或者倒过来取数据(遍历时,总是使用node方法获取节点中的元素),数据量大的时候用for循环的效率十分低下,耗费时间是线性叠加的,遍历链表,使用迭代器或者foreach最好