White set of Java source Learning Series: LinkedList

LinkedList source of learning

The foregoing Portal : the Java source code of the set white Learning Series: ArrayList

* This paper is a collection of source code learning series LinkedListlearning part, if any narrative inappropriate, but also hope the comments section criticism!

LinkedList inheritance system

LinkedList and ArrayList as are realized List interface represents the list structure, have similar add, remove, clear operation. The difference is that the ArrayList, based on the LinkedList bottom doubly linked list , allowing the storage of discontinuous addresses , establish contact through mutual reference between the nodes, data is stored by the node.
www.wityx.com

LinkedList core source

Since it is based on the node, then we have to look node in LinkedList what exists in:

    //Node作为LinkedList的静态内部类
    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;
        }
    }

We have found, as the Node inner class has three properties, is used a pointer to the preceding node prev , is a pointer pointing to a next node after , as well as the storage element values Item .
We take a look LinkedList several basic properties:

    /*用transient关键字标记的成员变量不参与序列化过程*/
    transient int size = 0;//记录节点个数

    /**
     * first是指向第一个节点的指针。永远只有下面两种情况:
     * 1、链表为空,此时first和last同时为空。
     * 2、链表不为空,此时第一个节点不为空,第一个节点的prev指针指向空
     */
    transient Node<E> first;

    /**
     * last是指向最后一个节点的指针,同样地,也只有两种情况:
     * 1、链表为空,first和last同时为空
     * 2、链表不为空,此时最后一个节点不为空,其next指向空          
     */
    transient Node<E> last;

    //需要注意的是,当first和last指向同一节点时,表明链表中只有一个节点。

After understanding the basic properties, we look at its construction method, since it does not have to care about the location of storage, its constructor is quite simple:

    //创建一个空链表
    public LinkedList() {
    }
    //创建一个链表,包含指定传入的所有元素,这些元素按照迭代顺序排列
    public LinkedList(Collection<? extends E> c) {
        this();
        //添加操作
        addAll(c);
    }

Which addAll (c) actually called addAll (size, c), where due to the size = 0, so add the equivalent of eleven from scratch. As addAll method, we do not mention, when we complete summary ordinary add operation, will naturally understand that all the added operation.

    //把e作为链表的第一个元素
    private void linkFirst(E e) {
        //建立临时节点指向first
        final Node<E> f = first;
        //创建存储e的新节点,prev指向null,next指向临时节点
        final Node<E> newNode = new Node<>(null, e, f);
        //这时newNode变成了第一个节点,将first指向它
        first = newNode;
        //对原来的first,也就是现在的临时节点f进行判断
        if (f == null)
            //原来的first为null,说明原来没有节点,现在的newNode
            //是唯一的节点,所以让last也只想newNode
            last = newNode;
        else
            //原来链表不为空,让原来头节点的prev指向newNode
            f.prev = newNode;
        //节点数量加一
        size++;
        //对列表进行改动,modCount计数加一
        modCount++;
    }

Accordingly, the added element is added as the first and the last element of the list element of the like, not detailed here. We take a look addAll operation we began to encounter, I feel a little bit of trouble oh:

    //在指定位置把另一个集合中的所有元素按照迭代顺序添加进来,如果发生改变,返回true
    public boolean addAll(int index, Collection<? extends E> c) {
        //范围判断
        checkPositionIndex(index);
        //将集合转换为数组,果传入集合为null,会出现空指针异常
        Object[] a = c.toArray();
        //传入集合元素个数为0,没有改变原集合,返回false
        int numNew = a.length;
        if (numNew == 0)
            return false;
        //创建两个临时节点,暂时表示新表的头和尾
        Node<E> pred, succ;
        //相当于从原集合的尾部添加
        if (index == size) {
            //暂时让succ置空
            succ = null;
            //让pred指向原集合的最后一个节点
            pred = last;
        } else {
            //如果从中间插入,则让succ指向指定索引位置上的节点
            succ = node(index);
            //让succ的prev指向pred
            pred = succ.prev;
        }
        //增强for循环遍历赋值
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            //创建存储值尾e的新节点,前向指针指向pred,后向指针指向null
            Node<E> newNode = new Node<>(pred, e, null);
            //表明原链表为空,此时让first指向新节点
            if (pred == null);
                first = newNode;
            else
                //原链表不为空,就让临时节点pred节点向后移动
                pred.next = newNode;
            //更新新表的头节点为当前新创建的节点
            pred = newNode;
        }
        //这种情况出现在原链表后面插入
        if (succ == null) {
            //此时pred就是最终链表的last
            last = pred;
        } else {
            //在index处插入的情况
            //由于succ是node(index)的临时节点,pred因为遍历也到了插入链表的最后一个节点
            //让最后位置的pred和succ建立联系
            pred.next = succ;
            succ.prev = pred;
        }
        //新长度为原长+增长
        size += numNew;
        modCount++;
        return true;
    }
  • Note: traverse the assignment is equivalent to starting from this temporary pred node, and then click Create a new node back and pred move backward until the new incoming final element of the collection, then two and then establish contact pred and succ seamless link.

Let's look at the list of ordinary delete element operation is how:

    //取消一个非空节点x的连结,并返回它
    E unlink(Node<E> x) {
        //同样的,在调用这个方法之前,需要确保x不为空
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        //明确x与上一节点的联系,更新并删除无用联系
        //x为头节点
        if (prev == null) {
            //让first指向x.next的临时节点next,宣布从下一节点开始才是头
            first = next;
        } else {
            //x不是头节点的情况
            //让x.prev的临时节点prev的next指向x.next的临时节点
            prev.next = next;
            //删除x的前向引用,即让x.prev置空
            x.prev = null;
        }
        //明确x与下一节点的联系,更新并删除无用联系
        //x为尾节点
        if (next == null) {
            //让last指向x.prev的临时节点prev,宣布上一节点是最后的尾
            last = prev;
        } else {
            //x不是尾节点的情况
            //让x.next的临时节点next的prev指向x.prev的临时节点
            next.prev = prev;
            //删除x的后向引用,让x.next置空
            x.next = null;
        }
        //让x存储元素置空,等待GC宠信
        x.item = null;
        size--;
        modCount++;
        return element;
    }

In conclusion, nothing more than delete, eliminate contact the node with two nodes, and let it establish a link between two adjacent nodes. , Such as a case where the head node and tail node, plus the need to analyze it in consideration of the boundary conditions. In short, it does not need to ArrayList, like copy arrays, but changed address references between nodes . However, the need to find before deleting this node, we still need to traverse drops, like this:

    //移除第一次出现的元素o,找到并移除返回true,否则false
    public boolean remove(Object o) {
        //传入元素本身就为null
        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) {
                //删除的元素不为null,比较值的大小
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

To sum up front to back traversal process:

  • Create a temporary node point first.
  • Backward traversal, where temporary node pointing to it next.
  • Temporary node until the next point to the last (ie, x == null) so far.

Of course, exceptional cases special consideration, the above remove method is to find the object corresponding to the element requiring only the addition of appropriate logic can be determined in the circulation. The following is a very important auxiliary method to obtain a node on the specified location by traversing : With this method, we can too with its rear position, the other is derived in different ways:

    //获得指定位置上的非空节点
    Node<E> node(int index) {
        //在调用这个方法之前会确保0<=inedx<size
        //index和size>>1比较,如果index比size的一半小,从前向后遍历
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            //退出循环的条件,i==indx,此时x为当前节点
            return x;
        } else {
            //从后向前遍历
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

At the same time there indexOf and lastIndexOf methods are summarized by the traversal above, with count condition, calculates the first specified element or an index of the last occurrence, here to indexOf Example:

    //返回元素第一次出现的位置,没找到就返回-1
    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) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

In fact, we talked about above traversal operation Well, the big difference is not bad. With this method, we can easily derive further contains method.

    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

Then it was on gay methods: GET and the SET .

    //获取元素值
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    //用新值替换旧值,返回旧值
    public E set(int index, E element) {
        checkElementIndex(index);
        //获取节点
        Node<E> x = node(index);
        //存取旧值
        E oldVal = x.item;
        //替换旧值
        x.item = element;
        //返回旧值
        return oldVal;
    }

Next is our clear method removes all elements of the table blank. Although the wording is different, but the basic idea is the same: to create a node and move, delete unwanted, or find the need, on the line .

    public void clear() {
        for (Node<E> x = first; x != null; ) {
            //创建临时节点指向当前节点的下一位
            Node<E> next = x.next;
            //下面就可以安心地把当前节点有关的全部清除
            x.item = null;
            x.next = null;
            x.prev = null;
            //x向后移动
            x = next;
        }
        //回到最初的起点
        first = last = null;
        size = 0;
        modCount++;
    }

Deque related operations

We also know, LinkedList also inherited the Deque interface that allows us to operate as queues operate it, here are some ways incomplete interception:
www.wityx.com

We pick and choose a few to analyze, with a few differences deceptive methods, such as the following four:

public E element() {
    return getFirst();
}
public E getFirst() {
    final Node<E> f = first;
    //如果头节点为空,抛出异常
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}
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;
}
  • Element : call getFirst method, if the head node is empty, an exception is thrown.
  • getFirst : If the head node is empty, an exception is thrown .
  • PEEK : head node is empty, return null.
  • peekFirst : head node is empty, return null.

Similarly there are:

  • pollFirst pollLast methods and remove the head and tail nodes, if it is empty, return null.
  • removeFirst and removeFirst if it is empty, throw an exception.

If you are interested, you can look, in short, is relatively simple.

to sum up

  • Based on the underlying LinkedList doubly linked implemented, it does not require continuous storage memory , the reference address is formed by the contact between the interconnected nodes.

  • No index insertion position, for example inserted backwards, the time complexity is approximately O (. 1) , reflecting faster operation deletions . However, if you want to insert the specified location, or to be moved to the current position of the specified index, can be operated, it is approximately the time complexity O (n).

  • Linkedlist does not support fast random access, query slow .

  • Thread unsafe. Similarly, with regard to threading, later summarized learning.

Published 184 original articles · won praise 141 · Views 350,000 +

Guess you like

Origin blog.csdn.net/sinat_27535209/article/details/104021451