【源代码】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接口,能够当成栈、队列、双端队列来使用。









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

posted @ 2015-08-14 21:34  blfshiye  阅读(158)  评论(0编辑  收藏  举报