LinkedList源码分析(JDK1.8)

2018-04-12  本文已影响19人  阿狸404

前面分析过ArrayList的源码,现在来看看LinkedList的实现原理。首先要知道为什么有了ArrayList的存在,为什么还需要LinkedList呢?

  1. LinkedList设计目的。

从上面知道ArrayList源码知道,ArryaList通过数组下标访问元素,查询效率快,但是ArrayList由于底层是数组实现的,物理地址是连续的,因此删除和添加由于都要移动复制数据,时间复杂度就提高了。为了解决这个问题,一次才有了这种物理地址不连续的数据结构的出现。LinkedList底层是结点,不要求物理地址连续,因此在删除,添加等操作效率比较高,但是查找元素效率么有ArrayList高,因为在LinkedList中查找元素基本上需要遍历整个链表。
我们来具体看看LinkedList具体实现。

  1. LinkedList设计原理
     /**
     * 结点,可以看做一个人,拥有左右手,左手表示上一个结点,右手表示下一个结点
     * 
     * @author wxe
     * @since 1.0.0
     */
    private static class Node<E> {
        E item;// 结点保存的元素
        Node<E> next; // 指向下一个结点
        Node<E> prev; // 指向上一个结点

        Node(Node<E> prev, E element, Node<E> next) {
            this.prev = prev;
            this.item = element;
            this.next = next;
        }
    }

结点如图所示:


图片.png
    transient Node<E> first;// 头结点

    transient Node<E> last; // 尾结点

    transient int size = 0;// 链表大小

    protected transient int modCount = 0;

    public LinkedList() {

    }

结构如下图所示:


1.0.png
/**
     * 添加元素,一般都是像尾结点处添加
     */
    public boolean add(E e) {
        linkLast(e);// 由此可以看出,添加不需要扩容,不需要复制操作,因此其添加元素效率高于ArrayList
        return true;
    }

添加结点到尾结点处,我们首先需要创建这个新的结点final Node<E> newNode = new Node<E>(l, e, null);而这个新的结点即将作为链表的尾结点。

     public void linkLast(E e) {
        final Node<E> l = last;// 获取最后一个结点,防止修改last,因此定义一个final域

        final Node<E> newNode = new Node<E>(l, e, null);// 创建即将添加的结点,也就是即将成为的最后一个结点

        last = newNode;// 尾指针指向新的结点

        if (l == null) {
            // 如果之前是空链表, 新建的节点也是第一个节点,相当于添加了一个头结点
            first = newNode;
        } else {
            // 如果原来有尾结点,则更新尾结点
            l.next = newNode;
        }

        size++;
        modCount++;
    }
public void linkFirst(E e) {
        final Node<E> f = first;
        // 第一步:创建新节点
        Node<E> newNode = new Node<E>(null, e, f);
        // 第二步:添加的结点成为新的头结点
        first = newNode;
        // 第三步:新的结点指向下一个结点
        if (f == null) {
            // 空链表的时候
            last = newNode;
        } else {
            f.prev = newNode;
        }
        size++;
        modCount++;
    }
/**
     * 指定位置添加元素,就只能遍历链表
     */
    public void add(int index, E element) {
        // 第一步:检查index是否越界
        checkPositionIndex(index);

        // 第二步:添加。如果是向尾结点添加元素,直接添加
        if (index == size) {
            linkLast(element);
        } else {
            // 如果不是向尾结点添加
            linkBefore(element, index);
        }
    }
    private void checkPositionIndex(int index) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("越界!");
        }
    }
/**
     * 向指定位置添加元素
     * 
     * @param element
     */
    public void linkBefore(E element, int index) {
        // 第一步:先找到index位置处的结点
        Node<E> oldNode = node(index);

        // 第二步:查找到index位置处的前后结点
        Node<E> prev = oldNode.prev;

        // 第三步:创建一个新结点,新节点的前驱结点是oldNode的前驱结点,后驱结点就是oldNode
        Node<E> newNode = new Node<E>(prev, element, oldNode);
        // 第四步:添加结点。主要分为四个动作:新建结点的时候:newNode指向oldNode的前驱结点,newNode后去结点指向oldNode。
        // 而oldNode的前驱结点指向新建结点newNode。oldNode结点原来的前驱结点的后去结点必须指向newNode
        oldNode.prev = newNode;
        if (prev == null) {
            // 如果添加的正好是头结点
            first = newNode;
        } else {
            // 添加的不是头结点
            prev.next = newNode;
        }
        // 第五步:容量大小+1
        size++;
        modCount++;
    }
/**
     * 查找指定位置index处的结点,采用二分查找
     * 
     * @param index
     * @return
     */
    public Node<E> node(int index) {
        // 采用二分搜索查找:如果index在size/2的前半部分,则从前向后开始遍历。否则,从后向前开始遍历
        int right = size >> 1;// 相当于size/2,采用>>主要考虑到效率
        if (index < right) {
            // 如果小于size/2,则在0-index之间查找
            Node<E> x = first;
            for (int i = 0; i < index; i++) {
                x = x.next; // 将x指针向后移动,开始遍历
            }

            return x;
        } else {
            // 如果大于size/2,则在index-size之间查找
            Node<E> x = last;
            for (int i = size - 1; i < index; i++) {
                x = x.prev;// 将x指针向前移动,开始遍历
            }

            return x;
        }
    }
/**
     * 移除指定元素
     */
    public boolean remove(Object o) {
        //LinkedList允许放置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) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
            
            return true;
        }
        return false;
    }
E unlink(Node<E> x){
        final E element = x.item;
        Node<E> prev = x.prev;
        Node<E> next = x.next;
        //第一步:处理前驱结点的引用
        if (prev == null) {
            //删除的是第一个结点
            first = next;
        } else {
            //删除的不是第一个结点,则x的前驱结点需要指向x的后驱结点,并且删除掉x前驱结点指向的prev
            prev.next = next;
            x.prev = null;
        }
        //第二步:处理后驱结点的引用
        if (next == null) {
            //如果删除的是尾结点,则当前尾结点是x的前驱结点
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        
        x.item = null;//便于垃圾回收器回收
        size --;
        modCount ++;
        return element;
    }
public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
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;
    }
 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;
    }
  1. LinkedList特性

LinkedList不仅实现了Collection接口,同时也实现了Queue,说明我们的LinkedList也是一种队列。队列的特性就是先进先出。而LinkedList由于结点的特殊定义,本身结点既知道下一个结点,也知道上一个结点,因此想要实现队列这种先进先出的特性,push的时候,调用addFirst(e)方法,而pop的时候,调用removeFirst(e)方法即可。
实现代码如下:

public void push(E e) {
        addFirst(e);
    }

 public void addFirst(E e) {
        linkFirst(e);
    }

 private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

出队列:

public E pop() {
        return removeFirst();
    }

 public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

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

先来看看迭代器的源码:

    private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;//期望修改的次数

        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }

        public boolean hasNext() {
            return nextIndex < size;
        }

        public E next() {
            checkForComodification();
            if (!hasNext())
                throw new NoSuchElementException();

            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.item;
        }

        public boolean hasPrevious() {
            return nextIndex > 0;
        }

        public E previous() {
            checkForComodification();
            if (!hasPrevious())
                throw new NoSuchElementException();

            lastReturned = next = (next == null) ? last : next.prev;
            nextIndex--;
            return lastReturned.item;
        }

        public int nextIndex() {
            return nextIndex;
        }

        public int previousIndex() {
            return nextIndex - 1;
        }

        public void remove() {
            checkForComodification();
            if (lastReturned == null)
                throw new IllegalStateException();

            Node<E> lastNext = lastReturned.next;
            unlink(lastReturned);
            if (next == lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = null;
            expectedModCount++;
        }

        public void set(E e) {
            if (lastReturned == null)
                throw new IllegalStateException();
            checkForComodification();
            lastReturned.item = e;
        }

        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null)
                linkLast(e);
            else
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (modCount == expectedModCount && nextIndex < size) {
                action.accept(next.item);
                lastReturned = next;
                next = next.next;
                nextIndex++;
            }
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)  //期望修改的次数和实际修改次数不一致时,就会抛出异常
                throw new ConcurrentModificationException();
        }
    }

这个迭代器的存在,主要是为了方便我们访问元素的。而我们知道LinkedList是线程不安全的。一旦有一个线程在改变LinkedList中的元素,而我们在访问的时候,就会通过modCount != expectedModCount比较知道当前多线程环境下集合元素又被修改了。主要原理就是我们在删除,修改LinkedList等操作时,修改了modCount ,而expectedModCount在迭代器中定义的,你访问的时候这个值还是以前的那个值,比较的时候两者不一致。就会导致抛出异常了。

  1. 内部结点定义使用静态内部类,这种方式创建对象不依赖外部对象,所以用起来更方便。
  2. 添加元素的时间为时间常数O(1),删除元素,不需要移动数据,单纯删除操作也是时间常数,所以删除,添加性能优于ArrayList。
  3. 随机访问指定下标的时间常数是线性的O(n),所以性能没有ArrayList的好。


    图片.png
上一篇 下一篇

猜你喜欢

热点阅读