LinkedList源码分析 (1.8)

       LinkedList也是Java容器中比较简单的存在,它的底层是采用双向链表来实现的。常用的LinkedList方法主要包括下面这些。add,addFirst,addLast(添加元素,在首部添加元素,在尾部添加元素),get,getFirst,getLast(获取元素,获取首部元素,获取尾部元素),indexOf,lastIndexOf(返回指定元素出现第一次的索引,置顶元素出现最后一次的索引),offer,offerFirst,offerLast(类似与add方法,实际上它的底层也是使用add方法来实现的),peek,peekFirst,peekLast(查看第一个元素,查看首部元素,查看最后一个元素),poll,pollFirst,pollLast(查看并删除元素),remove,removeFirst,removeLast(删除元素,删除首部元素,删除尾部元素)。而且这些方法其底层实现有很多是相通的,这里来简单分析一下比较基础的代码。

LinkedList重要参数和构造器

   LinkedList是由双向链表来实现的,整个类中包括首节点、尾节点以及链表长度的信息。

   

    /**
     *  记录整条链表的头结点
     */
    transient Node<E> first;

    /**
     * 记录整条链表的尾节点
     */
    transient Node<E> last;

    /**
     *一个空的构造器
     */
    public LinkedList() {
    }

    /**
     * 使用一个集合来当做参数的构造器
     *
     * @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);
    }
//存放首节点、尾节点和内容的节点类。
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中重要方法

1.链表链接的一些方法,这些方法是add,offer等方法的基础。


 /**
     * 在首部放置一个新的节点,首先F节点指向first节点,first节点指向新的节点,若不为空则f节点prev指向first。完成添加。
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)//如果f为空说明原链表本身为空,这里把last节点指向newNode即可。
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

    /**
     * 在尾部放置一个节点.首先l节点指向last,然后last节点指向newNode,若不为空则l.next指向last,完成添加。
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)//和上面类似,只是prev变成next,仍旧是为空则first和last指向同一个节点。
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

    /**
     * 在一个非空节点之前插入一个节点.
     */
    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);//构造新节点,前置节点为pred,后置节点为succ。
        succ.prev = newNode;//succ的前置节点更改为新产生的节点。
        if (pred == null)//如果succ原来的前置节点为空,那么把first指向newNode,也就是新的first节点。
            first = newNode;
        else//否则还需要把succ 的原来的前置节点的下一个节点连接上newNode
            pred.next = newNode;
        size++;//长度加一
        modCount++;//更改次数加一
    }

2.LinkedList删除方法比如poll、remove的底层实现。

 /**
     * 删除首节点。
     */
    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; // 置空可以帮助JVM回收这些废弃空间。
        first = next;//首节点是自己的下一个节点。
        if (next == null)//如果next为空,说明链已经空了,置last为null
            last = null;
        else//否则将next节点的上一个节点置为空。
            next.prev = null;
        size--;//长度减一
        modCount++;//操作次数加一
        return element;//返回节点元素
    }

    /**
     * 删除尾节点。和上面过程类似,只是将首节点,next更改为尾节点,prev。这里就不赘述了。
     */
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

    /**
     * 删除一个节点。
     */
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;//获取要删除节点的next节点
        final Node<E> prev = x.prev;//获取要删除节点的prev节点

        if (prev == null) {//如果prev节点为空,说明是首节点,那么first指向next即可
            first = next;
        } else {//否则的话,prev后置节点置为next,节点x的前置节点置空即可。
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {//如果next节点为空,说明是尾节点,那么last节点置为prev节点即可。
            last = prev;
        } else {//否则的话next节点的前置节点置为prev,节点x的next置为空,此时x节点就已经完全脱离链表了。
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

3.查找节点底层实现。

/**
     * 返回指定位置的节点
     */
    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;//最后返回找到的x节点
        }
    }

    // Search Operations

    /**
     * 返回指定元素第一次出现的索引。
     */
    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 {//正常值equals即可。
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

    /**
     * 和上面那个类似,只不过是从尾部开始一次遍历。
     */
    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;
    }

总结

       虽然LinkedList确实提供了很多方法,但是基本上大多数的方法都是在上面插入元素、查找元素、删除元素的基本方法上进行一些逻辑语句的添加,它们的核心方法还是上面的这些方法。而LinkedList本质上也只是一个链表,因此它的源码也都是对链表的一些操作,这里参考了一下1.7的代码,差别不是很大,或者可以说基本没有差别,有兴趣可以去看一下。另,有关链表可以结合自己画图来理解。



参考博客:https://blog.csdn.net/zw0283/article/details/51132161

                 https://www.cnblogs.com/goody9807/p/6437323.html

                  https://blog.csdn.net/anxpp/article/details/51203591

猜你喜欢

转载自blog.csdn.net/pb_yan/article/details/80244151