LinkedList源码部分解读

114 阅读3分钟

1、简介

LinkedList继承了AbstractSequentialList,实现了List<E>, Deque<E>, Cloneable, java.io.Serializable,是一种以双向链表为数据结构的集合。允许值为null。

  • 因为继承了AbstractSequentialList,所以它支持fail-fast的错误检测机制,错误检测机制见 集合部分知识点

  • 实现了Cloneable 接口,覆盖了函数 clone(),能被克隆。    

  • 实现java.io.Serializable 接口,这意味着支持序列化,能通过序列化去传输。 

  • 实现了List接口,覆盖了size()等方法

  • 实现了Deque接口,覆盖了addFirst、addLast等方法

    public class LinkedList extends AbstractSequentialList implements List, Deque, Cloneable, java.io.Serializable

同时,注意first节点和last节点的比较特殊的地方:

first、last可以同时为null,即这时候链表中只有一个空节点。

如果first、last节点不为null,first的前一个节点是null,last后一个节点是null。

/**
 * Pointer to first node.
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
    transient Node<E> first;

/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
    transient Node<E> last;

2、部分源码

2.1 构造方法

//空参构造,空链表
public LinkedList() {
}

//带参构造,参数为Collection,用已有的集合创建链表
public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
}

2.2 checkPositionIndex(int index)

//这两个函数的作用在于检查index是否在0和链表的长度之间,在则往下执行,否则抛出异常
private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
}

2.3 node(int index)

//该函数用来查询索引所在节点,很简单,不赘述
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;
        }
    }

2.4 addAll(Collection<? extends E> c)

public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
//见上面
        checkPositionIndex(index);
//将集合Collection转化为数组
        Object[] a = c.toArray();
        int numNew = a.length;
//如果数组长度为0,即集合为空,则直接返回
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
//如果将所有集合中的元素都加在链表末尾
        if (index == size) {
            succ = null;
            pred = last;
        } else {
//如果不是加到末尾,succ是index所在节点,pre为index的前一个节点
            succ = node(index);
            pred = succ.prev;
        }
将数组中的元素依次加入到链表中,index位置开始
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
//注意这里e的前一个节点是pred,所以后续中不用指定前一个节点,e的后一个节点为null
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }
//当succ==null说明所有的集合元素是加到末尾的,这时候只需要指明谁是最后一个节点
        if (succ == null) {
            last = pred;
        } else {
//否则,是加到链表中间的,则需要把数组最后一个加入到链表的节点 的下一个节点指向初始index所在节点
//初始index所在节点的前一个节点指向 数组最后一个加入到链表的节点
            pred.next = succ;
            succ.prev = pred;
        }
//增加LinkedList的size
        size += numNew;
        modCount++;
        return true;
}

2.5 add(E e)

//添加单个元素,直接加到链表的尾端
public boolean add(E e) {
        linkLast(e);
        return true;
}

void linkLast(E e) {
        final Node<E> l = last;
//指定e的前一个节点为链表的最后一个节点,后一个节点为null
        final Node<E> newNode = new Node<>(l, e, null);
//将last指向这个刚插入的新的节点
        last = newNode;
        if (l == null)
//l==null  说明空链表,则指定新的节点为头节点
//为什么不用指定尾节点?因为前面 last=newNode 啦
            first = newNode;
        else
//与链表的最后一个节点建立联系
            l.next = newNode;
        size++;
        modCount++;
}

2.6 linkLast(E e)

//用于添加节点到链表末端
void linkLast(E e) {
        final Node<E> l = last;
//e的前一个节点是last,后一个节点是null
        final Node<E> newNode = new Node<>(l, e, null);
//改变last
        last = newNode;
//如果一开始链表为null
        if (l == null)
//将新增的节点也指定为first
            first = newNode;
        else
//否则将l的下一个节点指向新的节点即可,因为新的节点的前一个节点已经在前面被指定(new Node步骤)
            l.next = newNode;
        size++;
        modCount++;
    }

2.7 linkBefore(E e, Node succ)

//将节点e添加到succ节点前面
void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
//记录succ的前一个节点pred
        final Node<E> pred = succ.prev;
//节点e的前一个节点为pred,后一个节点为succ,这时候还差两个箭头
        final Node<E> newNode = new Node<>(pred, e, succ);
//将succ的前一个节点指向新的节点
        succ.prev = newNode;
//如果前一个节点pred为null,说明这个链表只有一个节点,则指定新的节点为第一个节点
        if (pred == null)
            first = newNode;
        else
//否则pred的下一个节点指向新的节点
            pred.next = newNode;
        size++;
        modCount++;
}

2.8 add(int index, E element)

public void add(int index, E element) {
//检查索引是否合法,在0~size之间就合法,否则抛出异常
        checkPositionIndex(index);
//如果index==size,说明要加入到链表末尾
        if (index == size)
            linkLast(element);
        else
//否则,加入到index节点前
            linkBefore(element, node(index));
}

2.9 unlinkFirst(Node 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;
//头节点的下一个节点为null,说明原链表中只有一个节点,所以last要置为null
        if (next == null)
            last = null;
        else
//否则将新的头节点的前一个节点指向null
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

2.10 removeFirst()

public E removeFirst() {
        final Node<E> f = first;
//如果链表为null,即头节点为null,抛出异常
        if (f == null)
            throw new NoSuchElementException();
//否则断开头节点和其他点的联系
        return unlinkFirst(f);
}

2.11 remove()

public E remove() {
        return removeFirst();
}