JDK源码分析之集合03LinkedList

时间:2021-11-06 15:25:14

一、前言

    LinkedList是双向列表,实现方式是使用链表的方式保存元素;除了第一个和最后一个元素外,每一个节点都包含一个指向前一个和指向后一个节点的指针和元素的值。其特点是插入删除效率高,而随机访问效率低。

二、ArrayList源代码分析

2.1类的继承关系

//AbstractSequentialList实现了部分函数,Deque双端队列,支持从两端访问元素

public class LinkedList<E> extends AbstractSequentialList<E> implements

        List<E>, Deque<E>, Cloneable, java.io.Serializable

    2.2内部类

    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;

        }

    }

    /**

     * Adapter to provide descending iterators via ListItr.previous

     */

    private class DescendingIterator implements Iterator<E> {

        private final ListItr itr = new ListItr(size());

 

        public boolean hasNext() {

            return itr.hasPrevious();

        }

 

        public E next() {

            return itr.previous();

        }

 

        public void remove() {

            itr.remove();

        }

    }

    说明:此内部类对应保存数据的节点,包含泛型类型E的当前值,和两个Node类型的属性,分别指向前一个和后一个节点。第二个为从后遍历链表的迭代器,可以通过descendingIterator()接口获得

    2.3私有属性

    // 链表的长度,即元素的个数

    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;

说明:分别定义两个指向第一个元素和指向最后一个元素的指针,方便对链表的操作

2.4构造函数

    /**

     * 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.

     */

    // 创建一个包含指定集合中元素的链表

    public LinkedList(Collection<? extends E> c) {

        this();

        //调用addAll函数

        addAll(c);

    }

LinkedList提供了两种构造函数:创建一个空的链表和创建包含指定元素的链表,其中第二中方式是先创建一个空链表,然后再将所有元素添加到链表中,添加元素调用的addAll方法,其代码如下:

    public boolean addAll(Collection<? extends E> c) {

        return addAll(size, c);

    }

addAll函数调用的addAll(int index,Collection<? Extends E> 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.

     */

    public boolean addAll(int index, Collection<? extends E> c) {

        //检查index是否大于0,且小于本LinkedList对象的size

        checkPositionIndex(index);

        //集合转换为数组

        Object[] a = c.toArray();

        //要添加的新元素的个数

        int numNew = a.length;

        if (numNew == 0)

            return false;

 

        Node<E> pred, succ;

        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;

            //新建节点其中前一个节点指向pred

            Node<E> newNode = new Node<>(pred, e, null);

            //如果index==1

            if (pred == null)

                first = newNode;

            else

                pred.next = newNode;

            //pred指向添加进去的新节点

            pred = newNode;

        }

 

        if (succ == null) {

            //如果从末尾加入元素,则直接将last指向新加入的最后一个元素

            last = pred;

        } else {

            //如果不是从末尾添加元素,则将新添加的最后一个节点的next指向index节点。并把index节点的prev指向新添加的最后一个节点

            pred.next = succ;

            succ.prev = pred;

        }

 

        size += numNew;

        modCount++;

        return true;

    }

其中有调用node(int index)函数来获取指定索引位置的节点。其代码如下:

/**

* Returns the (non-null) Node at the specified element index.

*/

Node<E> node(int index) {

// assert isElementIndex(index);

//如果index小于size/2则从天查找

if (index < (size >> 1)) {

Node<E> x = first;

for (int i = 0; i < index; i++)

x = x.next;

return x;

} else {

//如果index的值大于size/2则从后面查找

Node<E> x = last;

for (int i = size - 1; i > index; i--)

x = x.prev;

return x;

}

}

2.5核心函数

1getFirst()函数

/**

     * Returns the first element in this list.

     *

     * @return the first element in this list

     */

    public E getFirst() {

        // 返回属性first

        final Node<E> f = first;

        if (f == null)

            throw new NoSuchElementException();

        return f.item;

    }

2unlinkFirst()函数

    /**

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

        // 设置为null,方便垃圾回收

        f.item = null;

        f.next = null; // help GC

        first = next;

        // 对特殊情况处理,如果只有一个元素,则last也设置为null;否则将新的首元素的prev设置为null

        if (next == null)

            last = null;

        else

            next.prev = null;

        // size1

        size--;

        modCount++;

        return element;

    }

3unlinkLast()函数

/**

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

        // 将要删除的节点设置为null,方便垃圾回收

        l.item = null;

        l.prev = null; // help GC

        // last指向删掉的节点的前一个节点

        last = prev;

        // 对特殊情况的处理:如果删掉的这个节点也是第一个节点,则first设置为null;否则将新的尾节点的next设置为null

        if (prev == null)

            first = null;

        else

            prev.next = null;

        size--;

        modCount++;

        return element;

    }

4linkFirst(E e)

/**

     * Links e as first element.

     */

    private void linkFirst(E e) {

        // 获取first元素节点

        final Node<E> f = first;

        // 创建新节点,next指向first节点

        final Node<E> newNode = new Node<>(null, e, f);

        // first引用指向新节点

        first = newNode;

        // 对特殊情况的处理:如果添加节点前的链表为空链表,则将last也指向新节点,否则,将之前的头节点的prev指向新的头节点

        if (f == null)

            last = newNode;

        else

            f.prev = newNode;

        // 增加链表的size

        size++;

        modCount++;

    }

5addLast(E e)

/**

     * Links e as last element.

     */

    void linkLast(E e) {

        // 获取last节点的引用

        final Node<E> l = last;

        // 创建新节点,其中prev指向last节点

        final Node<E> newNode = new Node<>(l, e, null);

        // last指向新节点

        last = newNode;

        // 对特殊情况的处理:如果添加前为空链表,则将first也指向新节点,否则将lnext指向新节点

        if (l == null)

            first = newNode;

        else

            l.next = newNode;

        // size1

        size++;

        modCount++;

    }

6indexOf(Object o)

    public int indexOf(Object o) {

        int index = 0;

        if (o == null) {

            // 对链表遍历

            for (Node<E> x = first; x != null; x = x.next) {

                if (x.item == null)

                    return index;

                index++;

            }

        } else {

            for (Node<E> x = first; x != null; x = x.next) {

                // 使用equals方法为判断是否相等的依据

                if (o.equals(x.item))

                    return index;

                index++;

            }

        }

        return -1;

    }

7unlink(Node<E> x)

    /**

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

        // 分别对特殊情况的判断:如果删除的为头节点,first指向next;否则将前一个节点的next指向next节点,将xprev设置为null

        if (prev == null) {

            first = next;

        } else {

            prev.next = next;

            x.prev = null;

        }

        // 如果删除的节点为尾节点,则将last指向倒数第二节点,否则,将下一个节点的prev指向前一个节点

 

        if (next == null) {

            last = prev;

        } else {

            next.prev = prev;

            x.next = null;

        }

 

        x.item = null;

        size--;

        modCount++;

        return element;

    }

8linkBefore(E e,Node<E> succ)

    /**

     * Inserts element e before non-null Node succ.

     */

    void linkBefore(E e, Node<E> succ) {

        // assert succ != null;

        // 先获取目标节点的前一个节点引用

        final Node<E> pred = succ.prev;

        // 创建新节点,prev指向目标节点的前一个节点,next指向目标节点

        final Node<E> newNode = new Node<>(pred, e, succ);

        succ.prev = newNode;

        // 特殊情况的处理,如果在头节点前添加元素,则将first指向新添加的节点,否则将前一个节点的next指向新节点

        if (pred == null)

            first = newNode;

        else

            pred.next = newNode;

        size++;

        modCount++;

    }

9lastIndexOf(Object o)

    public int lastIndexOf(Object o) {

        // 从最后一个开始遍历

        int index = size;

        if (o == null) {

            for (Node<E> x = last; x != null; x = x.prev) {

                index--;

                if (x.item == null)

                    return index;

            }

        } else {

            for (Node<E> x = last; x != null; x = x.prev) {

                index--;

                if (o.equals(x.item))

                    return index;

            }

        }

        return -1;

    }

10、双端队列的一些函数

JDK源码分析之集合03LinkedList

说明:LinkedList同时也实现了Deque接口,因此LinkedList可以用作双端队列,支持在链表两边的添加删除操作;接口列表如上图