LinkedList源码解析

1.继承的父类与实现的接口

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

 

2.类的属性

transient int size = 0;

transient Node<E> first;

transient Node<E> last;

在LinkedList中,定义了如上三个属性。

int size:集合的元素数量
Node<E> first:链表的头元素
Node<E> last:链表的尾元素

 

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

LinkedList的内部类,用以表示一个链表节点。

E item:当前元素
Node<E> next:下一个元素
Node<E> prev:上一个元素
Node(Node<E> prev, E element, Node<E> next):唯一构造函数。每创建一个节点,必须带有前一个节点,后一个节点,以及该节点内存放的元素。

 

4.类的构造函数

public LinkedList() {}

LinkedList():无参构造函数。

 

    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
LinkedList(Collection<? extends E> c):有参构造函数,参数类型为Collection<? extend E>。

由上代码可见,有参构造函数,其实是调用了无参构造函数,以及类中的addAll(Collection)方法,将参数传递进去。

    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

而addAll(Collection)方法,内部又是调用了addAll(int,Collection)方法。

    public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);  //校验index是否下标越界。

        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)  //没有添加新的元素,返回false
            return false;

        Node<E> pred, succ;  //建立前节点,后节点
        if (index == size) {  //index==size,说明是在当前集合的末尾插入新数据,因此没有后节点,succ=null,前节点为当前集合的最后一个节点pred=last
            succ = null;
            pred = last;
        } else {
            succ = node(index);  //找到当前下标指代的节点,要在该节点前插入数据,因此令succ等于该节点。
            pred = succ.prev;  //pred则等于succ前面一位的节点。需要注意当index=0时,该点可以为null。
        }

        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;  //强制类型转换Object转为E
            Node<E> newNode = new Node<>(pred, e, null);  //创建一个新的节点,节点元素为e,前节点为pred,后节点为null。
            if (pred == null)  //说明index=0,因此新插入的集合的第一个元素,作为first
                first = newNode;
            else
                pred.next = newNode;  //说明index<>0,因此新的节点,作为前节点的后节点(pred.next)
            pred = newNode;  //令当前节点作为前节点,获取下一个元素,循环。
        }

        if (succ == null) {  //说明是从当前集合的末尾开始插入数据,因此数据的最后一个元素,作为当前集合的last
            last = pred;
        } else {  //pred为新插入的最后一个数据,令其的后节点等于之前拆开位置的后节点,succ为之前拆开位置的前节点,令其前节点prev等于新插入的元素的最后一个数据。
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;  //新插入numNew条数据,size增加相应的大小。
        modCount++;
        return true;
    }

在addAll(int,Collection)方法中,首先执行checkPositionIndex(int)检验index的合法性(是否在当前对象的size范围内)。

    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

 

5.总结

LinkedList重点在于对内部类Node<E>的理解。

每一个元素在LinkedList中都是在Node<E>中存储,每个Node<E>中同时还存储了当前元素的前节点和后节点。

新增元素或集合,只要找到添加的位置,获取该位置的前节点pred,和后节点succ,令pred.next=新插入集合的第一个元素节点,令succ.pred=新插入的集合的最后一个元素节点即可。

删除元素或集合,找到删除的位置,获取该位置的前节点pred,和后节点succ,令pred.next=succ.pred即可。

注意不论是新增还是删除,均要考虑到起始节点没有pred和结束节点没有next的问题。

每一个LinkedList对象中均只存了first和last两个节点,因此当根据索引寻找元素时,代码会判断索引是在前半部分还是后半部分,从而决定从first出发,还是从last出发。

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

 

 

 

 

 

pred等于该节点的前一个节点。
posted @ 2019-03-02 23:36  一响贪欢  阅读(643)  评论(0编辑  收藏  举报