zoukankan      html  css  js  c++  java
  • 【源代码】LinkedList源代码分析

    //-----------------------------------------------------------
    转载请注明出处:http://blog.csdn.net/chdjj
    by Rowandjj
    2014/8/8
    //----------------------------------------------------------

    注:下面源代码基于jdk1.7.0_11

    上一篇我们分析了ArrayList,今天我们再来看下LinkedList。

    首先上一幅框架图:


    LinkedList相同间接继承了AbstractList抽象类,对外来看,LinkedList提供的操作接口跟ArrayList是非常类似的。区别在于内部实现上。略微有点基础的都知道,LinkedList是基于双向链表这样的数据结构,而ArrayList上一篇已经分析过了。是通过数组实现的。

    我们依然依照之前的思路,自顶向下分析,AbstractList以及其上面的类或接口我们上一篇已经分析过,这里不再反复,我们从AbstractSequentialList開始。

    package java.util;
    public abstract class AbstractSequentialList<E> extends AbstractList<E> {
        protected AbstractSequentialList() {//仅仅有一个构造器
        }
       public E get(int index) {//获取指定位置的值
            try {
                return listIterator(index).next();//通过迭代器的方式
            } catch (NoSuchElementException exc) {//找不到就抛出异常
                throw new IndexOutOfBoundsException("Index: "+index);//这是个Runtime异常
            }
        }
    
        public E set(int index, E element) {
            try {
                ListIterator<E> e = listIterator(index);//相同调用的listiterator
                E oldVal = e.next();//记录
                e.set(element);
                return oldVal;//返回
            } catch (NoSuchElementException exc) {
                throw new IndexOutOfBoundsException("Index: "+index);
            }
        }
        public void add(int index, E element) {
            try {
                listIterator(index).add(element);
            } catch (NoSuchElementException exc) {
                throw new IndexOutOfBoundsException("Index: "+index);
            }
        }
    
        public E remove(int index) {
            try {
                ListIterator<E> e = listIterator(index);
                E outCast = e.next();
                e.remove();
                return outCast;
            } catch (NoSuchElementException exc) {
                throw new IndexOutOfBoundsException("Index: "+index);
            }
        }
        // Bulk Operations
        public boolean addAll(int index, Collection<?

    extends E> c) { try { boolean modified = false; ListIterator<E> e1 = listIterator(index); Iterator<? extends E> e2 = c.iterator(); while (e2.hasNext()) { e1.add(e2.next()); modified = true; } return modified; } catch (NoSuchElementException exc) { throw new IndexOutOfBoundsException("Index: "+index); } } // Iterators public Iterator<E> iterator() { return listIterator(); } public abstract ListIterator<E> listIterator(int index);//參数为索引位置。表示从哪開始遍历 }

    能够发现。这个抽象类中的方法都依赖于这个ListIterator迭代器,而这个获取迭代器的方法是抽象的,留给子类完毕。另外iterator方法并没有返回iterator,而相同是返回了listiterator对象。

    接下来,我们分析LinkedList。

    先看声明:
    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    须要注意的是LinkedList实现了Deque接口,这个接口代表一个双端队列,内部封装了双端队列的全部操作,故而LinkedList能够当做一个栈、队列或者是双端队列来使用
    以下是其成员变量:
     transient int size = 0;//集合大小(结点个数)
     transient Node<E> first;//头指针
     transient Node<E> last;//尾指针
    前面说过。linkedList是通过双向链表实现,故而不须要有扩容的方法,由于结点是动态申请的。

    而这个结点的类型即为Node。以下看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构造器:
     public LinkedList() {}
     public LinkedList(Collection<?

    extends E> c) { this(); addAll(c); }

    再看一些对结点的操作方法:
    假设你熟悉双向链表。就会发现以下几个函数非常easy,无非是处理指针的指向问题。
     private void linkFirst(E e) {//插到头部
            final Node<E> f = first;
           //创建一个新结点,前驱为空。后继为f(也就是当前的头结点)
            final Node<E> newNode = new Node<>(null, e, f);//注意这样的泛型的写法也是能够的
            first = newNode;//头指针指向新结点
            if (f == null)//链表为空时
                last = newNode;//尾指针指向新结点
            else//否则
                f.prev = newNode;//让f的前驱指向新结点
            size++;
            modCount++;
        }
        void linkLast(E e) {//插到尾部
            final Node<E> l = last;//暂时变量记录尾结点
            final Node<E> newNode = new Node<>(l, e, null);//创建新结点,前驱为l
            last = newNode;//更新尾指针
            if (l == null)//假设链表为空
                first = newNode;
            else
                l.next = newNode;
            size++;
            modCount++;//用于高速失败机制
        }
    
        void linkBefore(E e, Node<E> succ) {//将e插入succ之前
            // assert succ != null;//调用者须要保证succ不为空
            final Node<E> pred = succ.prev;//记录succ的前驱
            final Node<E> newNode = new Node<>(pred, e, succ);//新结点的前驱指向succ的前驱,新结点的后继指向succ
            succ.prev = newNode;//succ的前驱指向新结点
            if (pred == null)//succ为头结点
                first = newNode;//更改头指针
            else
                pred.next = newNode;//否则succ的前驱的后继指向新结点
            size++;
            modCount++;
        }
        private E unlinkFirst(Node<E> f) {//干掉头结点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 E unlinkLast(Node<E> l) {//干掉尾结点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) {//干掉一个普通结点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;
        }
    有了这些基本函数之后。实现其它操作就方便了。比方这些:
     public void addFirst(E e) {
            linkFirst(e);
        }
       public void addLast(E e) {
            linkLast(e);
        }
      public boolean add(E e) {
            linkLast(e);
            return true;
        }
    再看这个remove方法:

     public boolean remove(Object o) {
            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 false;
        }

    跟ArrayList类似,依据參数是否为null,进行了两种处理,说明LinkedList也是支持null的元素的

    再看清空操作:
     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 = next;
            }
            first = last = null;
            size = 0;
            modCount++;
        }
    以下的函数封装了索引链表位置的操作:
     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;
            }
        }
    这个函数用了一个小技巧。首先推断待索引的位置是在链表前半部分还是后半部分,若是前半部分,则顺序索引。否则逆序索引(这就是双向链表的长处之中的一个)。

    之前在AbstractSequentialList中未实现的方法在这里得到了实现:
     public ListIterator<E> listIterator(int index) {
            checkPositionIndex(index);
            return new ListItr(index);
        }

    这个ListItr是LinkedList的内部类,实现了ListIterator接口。

    private class ListItr implements ListIterator<E>  
      private Node<E> next;
      ListItr(int index) {
                // assert isPositionIndex(index);
                next = (index == size) ? null : node(index);
                nextIndex = index;
            }

    通过构造器指定起始遍历的位置,内部通过调用node方法索引该位置的对象。详细方法限于篇幅不在介绍。
    值得一提的是这个类还提供了一个反向的迭代器
    public Iterator<E> descendingIterator() {
            return new DescendingIterator();
        }

    这个反向迭代器事实上是对上面介绍的ListItr的封装。


    总结:
    1.LinkedList内部通过双向链表实现;
    2.LinkedList支持null元素;
    3.LinkedList插入删除元素较方便。可是查找操作较耗时(对照ArrayList)。尽管内部进行了优化(依据位置选择顺序还是逆序遍历);
    4.LinkedList内部相同通过内部类的形式实现了迭代器(仅实现了ListIterator,iterator方法返回的也是ListIterator对象)。
    5.LinkedList实现了Deque接口,能够当成栈、队列、双端队列来使用。









    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    Alchemy解决方案使得大型主机应用程序能在Windows Azure上运行
    Hello China操作系统微博:http://weibo.com/2196920292
    GDI+ 学习记录(12): 矩形 Rectangle、Rectangles
    GDI+ 学习记录(6): 使用画刷建立画笔
    GDI+ 学习记录(8): 阴影画刷 HatchBrush
    GDI+ 学习记录(5): 复合画笔
    GDI+ 学习记录(11): 路径渐变画刷 PathGradientBrush
    GDI+ 学习记录(7): 实心画刷 SolidBrush
    GDI+ 学习记录(4): 画笔对齐
    GDI+ 学习记录(13): 弧线 Arc
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4731276.html
Copyright © 2011-2022 走看看