LinkedList 基于源码学习

179 阅读8分钟

概括

  • LinkedList是基于链式结构,同时实现了队列的相关接口, 包括队头队尾的插入,队头队尾的取值等等。
  • LinkedList因为同时实现了List和Queue两个接口,因此同时拥有了链表的特性和队列的特性。而LinkedList的方法也因此分成了两大类:
    • 链表类型方法:add系列,remove系列。
    • 队列类型方法:poll系列,peek系列,offer系列。
  • LinkedList还是实现了两个系列的方法:
    • link系列,是add系列和offer系列的核心实现方法。
    • unlink系列,是poll系列和remove系列的核心实现方法。

LinkedList属性及通用方法

/**
 * 链表保存的元素数量
 */
transient int size = 0;

/*
 * 第一个节点
 */
transient Node<E> first;

/**
 * 最后一个节点
 */
transient Node<E> last;

LinkedList内部类

Node

/**
 * 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;
    }
}

核心逻辑追踪

  • offer & add & offerLast & addLast: 队头插入元素。
    • linkLast: 队头插入元素核心实现。
  • offerFirst方法 & addFirst方法: 队尾插入元素。
    • linkFirst方法: 队尾插入元素核心实现。

插入元素

  • add系列:表示插入元素,属于链表类型的方法。
  • offer系列:表示插入元素到链头链尾,属于队列类型的方法。
  • link系列:核心实现插入元素的方法。

add系列

add开头的方法用于插入元素

/* add方法的核心逻辑都是调用link实现 */

/**
 * 插入元素
 */
public boolean add(E e) {
    linkLast(e); // 插入到队尾
    return true;
}
/**
 * 插入元素到指定位置
 */
public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        // 把元素插到指定元素前面
        linkBefore(element, node(index));
}
/**
 * 插入到队头
 */
public void addFirst(E e) {
    linkFirst(e);
}
/**
 * 插入到队尾
 */
public void addLast(E e) {
    linkLast(e);
}

offer系列

offer系列方法用于插入元素到链头链尾。

/* offer方法的核心逻辑都是调用add实现 */
public boolean offer(E e) {
    return add(e);
}
public boolean offerFirst(E e) {
    addFirst(e);
    return true;
}
public boolean offerLast(E e) {
    addLast(e);
    return true;
}

link系列

/**
 * 把指定元素连接到某节点前面。
 */
void linkBefore(E e, Node<E> succ) {
    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++;
}
/**
 * 把元素连接到队头。
 */
private void linkFirst(E e) {
    final Node<E> f = first;
    // 封装成node
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    if (f == null)
        last = newNode;
    else
        // 把原头节点的前指针指向新的元素
        f.prev = newNode;
    size++;
    modCount++;
}
/**
 * 把元素e连接到队尾。
 */
void linkLast(E e) {
    final Node<E> l = last; // 获取队尾
    final Node<E> newNode = new Node<>(l, e, null); // 把e封装成节点
    last = newNode;
    // 队尾为空,表示队列是空的
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

删除元素

  • remove系列:对外开发的删除元素接口,属于链表类型接口。
  • unlink类:内部的核心移除节点方法,一般被删除元素的方法所调用。

remove系列

remove方法 & removeFirst方法 & removeLast方法

remove开头的方法都是移除指定的元素

/**
 * 移除队列第一个元素
 */
public E remove() {
    return removeFirst();
}
/**
 * 移除从头数第index个元素
 */
public E remove(int index) {
    checkElementIndex(index); // 确保索引没有溢出
    // 通过调用unlink方法实现移除操作
    return unlink(node(index)); 
}
/**
 * 把指定元素节点从链表中删除
 */
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;
}
/**
 * 移除第一个元素
 */
public E removeFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    // 通过调用unlink方法实现移除操作
    return unlinkFirst(f);
}
/**
 * 移除链表最后一个节点
 */
public E removeLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return unlinkLast(l);
}

node方法

/**
 * 获取指定index索引的节点
 */
Node<E> node(int 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;
    }
}

unlink类

unlink方法 & unlinkFirst方法 & unlinkLast方法

核心删除节点的逻辑。

/**
 * 把节点从链表中剔除
 */
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) {
        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--;
    modCount++;
    return element;
}

/**
 * 把第一个节点从链尾断开
 */
private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // 方便垃圾回收
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

/**
 * 把最后一个节点从链尾断开
 */
private E unlinkLast(Node<E> l) {
    final E element = l.item;
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // 方便垃圾回收
    last = prev;
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}

获取元素

  • get系列:获取元素方法,属于链表类型接口
  • peek系列:获取链头链尾元素的方法,属于队列类型的接口。
  • poll系列:获取并移除链头链尾元素的方法,属于队列类型的接口

get系列

get开头的方法都用于获取元素。

/**
 * 获取指定索引的元素
 */
public E get(int index) {
    checkElementIndex(index);
    // node方法是顺序查找
    return node(index).item;
}
/**
 * 获取第一个节点的元素
 */
public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}
/**
 * 获取最后一个节点的元素
 */
public E getLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return l.item;
}


peek系列

peek开头的方法都是探测类型,只能获取链表头和链表尾的元素。

/**
 * peek开头的
 */
public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}
/**
 * 获取链表头的元素
 */
public E peekFirst() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

/**
 * 获取链表尾的元素
 */
public E peekLast() {
    final Node<E> l = last;
    return (l == null) ? null : l.item;
}

poll系列

/*poll系列表示获取队头或队尾的值,并把该值从链表移除*/

public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
public E pollFirst() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}

Iterate

每个List接口的实现类都必须要实现Iterate方法,并实现对应的Iterable实现类,从而可以方便的遍历列表元素。

iterator方法追踪

/**
 * LinkedList本身没有重写iterator方法
 * 这里展示的是AbstractSequentialList的iterator实现
 * AbstractSequentialList是LinkedList的父类,是一个抽象类。
 */
public Iterator<E> iterator() {
    return listIterator();
}
/**
 * 该方法在AbstractList实现
 * AbstractList是AbstractSequentialList的父类
 * AbstractList也是一个抽象类。
 */
public ListIterator<E> listIterator() {
    return listIterator(0);
}
/**
 * 该方法被LinkedList重写
 * @param index 表示从第几个元素开始遍历
 */
public ListIterator<E> listIterator(int index) {
    checkPositionIndex(index);
    // 这里遍历类是ListItr
    return new ListItr(index);
}

内部类ListItr

/**
 * ListItr通过记录要开始遍历的节点,并从该节点逐渐向后访问,
 * 从而实现了遍历器。
 */
private class ListItr implements ListIterator<E> {
    /**
     * 最后一个读取过值的节点
     */
    private Node<E> lastReturned;
    /**
     * 下一个要读取值得节点
     */
    private Node<E> next;
    /**
     * 下一个要读的值的索引,该索引就是next得索引。
     */
    private int nextIndex;
    /**
     * 生成遍历器时,modCount值
     * 该值用于判断集合在遍历期间是否被修改过
     * 原则上,遍历期间不允许修改集合元素
     */
    private int expectedModCount = modCount;

    ListItr(int 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 void remove() {
        checkForComodification();
        if (lastReturned == null)
            throw new IllegalStateException();

        Node<E> lastNext = lastReturned.next;
        unlink(lastReturned);
        
        // 如果next指向了已读的节点
        if (next == lastReturned)
            next = lastNext; // 指向已读节点的下一节点
        else
            nextIndex--; // 因为少了一个元素,所以索引值减1
        lastReturned = null;
        expectedModCount++;
    }

    /**
     * 判断集合是否修改过
     */
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}