LinkedList源码解读

419 阅读6分钟

LinkedList是双向链表,每个节点都指向前一个和后一个节点。

类图如下所示:

image.png

  • 与ArrayList相同的是,LinkedList也实现了List, Cloneable, java.io.Serializable这三个接口;
  • LinkedList不包含java.util.RandomAccess 接口,因此,它不支持随机访问。
  • LinkedList实现了Deque接口,提供了双端队列的功能,LinkedList 支持快速的在头尾添加元素和读取元素,所以很容易实现该特性。

1 源码解析

1.1 属性

//关键字transient可使属性不被序列化
transient int size = 0;

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

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

从代码中可知,LinkedList有三个属性:size,first,last。

  • first和last分别指链表的头尾指针。

    • 在初始时候,firstlast 指向 null ,因为此时暂时没有节点。
    • 在添加完首个节点后,创建对应的节点 node1 ,前后分别都指向 null 。此时,firstlast 指向该 节点。
    • 继续添加一个节点后,创建对应的 Node 节点 node2 ,其 prev = node1next = null ,而 node1prev = nullnext = node2 。此时,first 保持不变,指向 node1last 发生改变,指向 node2
  • size 属性:链表的节点数量。通过它进行计数,避免每次需要 List 大小时,需要从头到尾的遍历。

1.2 构造函数

/**
 * 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();
    addAll(c);
}

​ LinkedList有两个构造函数,与ArrayList不同的是,ArrayList提供了一个通过设置初始化容量来初始化类的构造函数。LinkedList底层是链表,添加新元素时,将新节点加入链表,容量随着节点的个数变化而变化。而ArrayList底层是数组,所以我们可以为ArrayList设置初始容量,也就是设置的数组的容量。

1.3 主要方法

1.3.1 get()方法获取指定位置的元素

/**
 * 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}
 */
public E get(int index) {
    //判断index是否越界
    checkElementIndex(index);
    return node(index).item;
}

 /**
     * Returns the (non-null) Node at the specified element index.
     */
    Node<E> node(int index) {
        // assert isElementIndex(index);
		//判断index是否小于链表长度的1/2,如果是,从前往后遍历,否则从后往前遍历(为了减少遍历次数)
        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;
        }
    }

1.3.2 set()方法设置指定位置的元素

/**
 * Replaces the element at the specified position in this list with the
 * specified element.
 *
 * @param index index of the element to replace
 * @param element element to be stored at the specified position
 * @return the element previously at the specified position
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E set(int index, E element) {
    //判断index是否越界
    checkElementIndex(index);
    //将指定节点中的item更新
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    //更新旧节点
    return oldVal;
}

1.3.3 添加新节点到LinkedList

(1)LinkedList中有两个add方法,用于添加单个元素

  • add(E e) 方法,顺序添加单个元素到链表。

  • add(int index, E element)方法,插入单个元素到指定位置。

    ​ 代码如下图所示:

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

/**
     * 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 the specified element at the specified position in this list.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

/**
     * Inserts element e before non-null Node succ.
     插入节点到链表中
     */
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        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++;
    }

(2)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);

    //判断是否新增集合为null
    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0)
        return false;

    //获取当前index位置的节点succ ,与它前一个节点pred
    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);
         // 如果 pred 为 null ,说明 first 也为 null ,则直接将 first 指向新节点
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;// pred.next指向新节点
        pred = newNode;// 修改pred指向新节点(pred指针右移一位)
    }

    if (succ == null) {// 如果 succ 为 null ,说明插入链表尾端,则直接修改 last 指向最后一个 pred
        last = pred;
    } else {
        //否则,新插入的最后一个节点的next指向succ,succ节点的prev指向新插入的最后一个节点
        pred.next = succ;
        succ.prev = pred;
    }

    size += numNew;
    modCount++;
    return true;
}

1.3.4 实现Deque接口的addFirst()和addLast()方法

​ addFirst(E e)方法即添加节点到链表首端,addLast(E e)方法即添加节点到链表尾端。

/**
 * Inserts the specified element at the beginning of this list.
 *
 * @param e the element to add
 */
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);
}

1.3.5push(E e)方法和Pop()

​ push(E e)方法用于将元素压入LinkedList表示的堆栈的开头(顶部)。

​ Pop()方法用于弹出位于链表所表示的堆栈顶部的对象。

/**
 * 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方法,将节点添加到链表首端
    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();
    }

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

 /**
     * Unlinks non-null first node f.
     */
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //获取首节点的元素和首节点的next node
        final E element = f.item;
        final Node<E> next = f.next;
        //让首节点元素和next node为null
        f.item = null;
        f.next = null; // help GC
        //令原首节点next node成为新的首节点
        first = next;
        //如果next为null(新的首节点为null),说明链表为null,因此last同样为null
        if (next == null)
            last = null;
        //如果next不为null,则新的首节点的prev指针应该指向null
        else
            next.prev = null;
        //将链表长度-1
        size--;
        //增加修改次数
        modCount++;
        //返回原首节点的元素,便有了弹出栈顶元素的效果
        return element;
    }

1.3.6 remove(Object)方法移除元素

public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            //从链表头开始遍历,找到第一个item为null的节点,移除
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
           //从链表头开始遍历,找到第一个item为o的节点,移除
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

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

        if (prev == null) {
            //如果移除节点为头结点,则直接将first指针指向头结点的下一个节点
            first = next;
        } else {
            //如果不是头结点
            //断开prev和x的链接
            prev.next = next;
            x.prev = null;
        }

        
        if (next == null) {
            //如果移除节点为尾结点,则直接将last指针指向头结点的上一个节点
            last = prev;
        } else {
             //如果不是尾结点
            //断开next和x的链接
            next.prev = prev;
            x.next = null;
        }
        //将节点中的元素设为null
        x.item = null;
        size--;
        modCount++;
        return element;
    }

1.4 其它方法

其他方法在LinkedList的主要方法中基本都有调用,具体使用方法见1.3。

1.4.1 node(int index)

 /**
     * Returns the (non-null) Node at the specified element index.
     */
    Node<E> node(int index) {
        // assert isElementIndex(index);
		//判断index是否小于链表长度的1/2,如果是,从前往后遍历,否则从后往前遍历(为了减少遍历次数)
        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;
        }
    }

1.4.2 linkBefore(E e, Node succ) ,linkLast(E e)

/**
     * Inserts element e before non-null Node succ.
     插入节点到链表中
     */
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        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++;
    }


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