【源码】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);
    }
再看一些对结点的操作方法:
如果你熟悉双向链表,就会发现下面几个函数很简单,无非是处理指针的指向问题。
 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接口,可以当成栈、队列、双端队列来使用。









  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值