美文网首页
jdk源码分析之LinkedList

jdk源码分析之LinkedList

作者: shoulda | 来源:发表于2018-05-03 19:41 被阅读0次

LinkedList关键属性:

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;

size属性表示当前链表有多少个元素,first指针指向链表的第一个元素,last指针指向链表的第二个元素。

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

可知LinkedList底层是一个双向链表,一个头指针,一个尾指针,还有一个数据域。

LinkedList首位添加数据linkFirst(E e)和linkLast(E e)

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

先保存就链表的尾指针,然后根据传入的e 和 l构建一个数据节点。如果l == null,则表示先前链表为空,并将刚构建的节点放入链表中,头指针和尾指针都指向该新建的节点。如果l != null,则表示先前链表中有数据,并将新构建的节点放到链表尾部。linkFirst(E e)源代码和它类似,原理相同。

LinkedList删除头结点unlinkFirst()和尾节点unlinkLast()

 private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

先保存头结点的后继节点,然后头结点指针域和数据域置null,加速内存回收速度,然后判断next是否为null,如果为null,证明现在把链表中唯一的节点删除了;若不为null,表示删除节点后链表还有数据,这时修改next的pre指针为null即可。unlinkLast()原理类似。

LinkedList删除某一节点unlink()

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

本质还是对指针的操作,先取得删除节点的数据域,后继和前继,然后进行断链操作。
前序为null,表示待删除节点是头结点,修改first指针为待删除节点的后继节点。
前序节点不为null就把前序节点的next指针指向后继节点,待删除节点的pre为null.
判断后继节点是否为null,是null的话表示待删除节点就是队尾节点,然后修改队尾指针last为待删除节点的前驱。
后继节点不为null,则后继节点的prev指针指向待删除节点的前序节点,待删除节点的next指针指向null.
最后把待删除节点的数据域置null.

LinkedList的某一个位置添加一个集合addAll

    public boolean addAll(int index, Collection<? extends E> c) {
        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 {
            succ = node(index);
            pred = succ.prev;
        }

        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }
private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

首先检查插入位置是否合法,可以队首插入,也可以队尾插入,中间任意位置当然也可以,然后把集合对象转化为数组,构建节点的前驱和后继。如果插入位置index==size的话表示队尾插入数据,因此后续为null,前序为队尾last不是在队尾插入集合的话需要根据插入位置index找到后继节点,找到了后继也就找到了前序,通过new Node<>(pred, e, null)创建新节点,前序指针在创建的时候在构造函数里边已经指明。 pred == null表示是在队首添加数据,需要修改队首指针first 否则把前序节点的后继指针设置为newNode
再把前序节点设为newNode,接着下一次循环 这样在循环结束后,只有最后一个添加的节点的后继指针没有处理,如果succ后继为null,修改队尾指针为last为最后一个添加的节点pred 否则设置最有一个添加的节点的后继指针为succ,succ的前序指针指向最后一个添加的节点pred 最后修改size和modCount,返回true表示添加成功。

相关文章

网友评论

      本文标题:jdk源码分析之LinkedList

      本文链接:https://www.haomeiwen.com/subject/nqvlrftx.html