【Java集合源码剖析】LinkedList源码剖析

来源:互联网 发布:淘宝客服电话是多少 编辑:程序博客网 时间:2024/06/01 09:41

推荐一篇分析LinkedList的博客,非常不错:传送门

LinkedList是基于双向链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈、队列和双端队列来使用。

LinkedList同样是非线程安全的,只在单线程下适合使用。

LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆。

源码如下。

public class LinkedList<E>        extends AbstractSequentialList<E>        implements List<E>, Deque<E>, Cloneable, java.io.Serializable{    // 链表尺寸    transient int size = 0;    /**     * 头结点     * Invariant: (first == null && last == null) ||     *            (first.prev == null && first.item != null)     */    transient Node<E> first;    /**     * 头结点     * Invariant: (first == null && last == null) ||     *            (last.next == null && last.item != null)     */    transient Node<E> last;    /**     * Constructs an empty list.     */    public LinkedList() {    }    /**     * 链接新元素到头部     */    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++;    }    /**     * 链接新元素到尾部     */    void linkLast(E e) {        // 记录在尾部添加元素前的尾指针        final Node<E> l = last;        // 创建新结点        final Node<E> newNode = new Node<>(l, e, null);        // 尾指针指向新节点        last = newNode;        // 如果链表还是空表,则头指针也指向新结点,否则将新结点链接到尾部        if (l == null)            first = newNode;        else            l.next = newNode;        // 尺寸增加        size++;        modCount++;    }    /**     * 在succ结点前插入元素e     */    void linkBefore(E e, Node<E> succ) {        // 记录succ结点的前驱        final Node<E> pred = succ.prev;        // 生成新结点,并链接到succ结点的前驱和succ结点本身。        final Node<E> newNode = new Node<>(pred, e, succ);        // 更新succ的直接前驱结点为新结点        succ.prev = newNode;        // 如果记录的succ结点的前驱为null,说明插入的位置位于头部,则让头指针指向新结点,否则更新其后驱为新结点        if (pred == null)            first = newNode;        else            pred.next = newNode;        // 尺寸增加        size++;        modCount++;    }    /**     * 断开头部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;    }    /**     * 断开尾部l结点的链接     */    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;    }    /**     * 断开结点x,并返回其item     */    E unlink(Node<E> 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 E getFirst() {        final Node<E> f = first;        if (f == null)            throw new NoSuchElementException();        return f.item;    }    /**     * 获取尾结点元素     */    public E getLast() {        final Node<E> l = last;        if (l == null)            throw new NoSuchElementException();        return l.item;    }    /**     * 移除头结点元素     */    public E removeFirst() {        final Node<E> f = first;        if (f == null)            throw new NoSuchElementException();        return unlinkFirst(f);    }    /**     * 移除尾结点元素     */    public E removeLast() {        final Node<E> l = last;        if (l == null)            throw new NoSuchElementException();        return unlinkLast(l);    }    /**     * 添加元素到头结点     */    public void addFirst(E e) {        linkFirst(e);    }    /**     *  添加元素到尾结点     */    public void addLast(E e) {        linkLast(e);    }    /**     * 添加指定的元素到链表的尾部.     * 和addLast()方法行为一致.     */    public boolean add(E e) {        linkLast(e);        return true;    }    /**     * 获取指定位置的元素     */    public E get(int index) {        checkElementIndex(index);        return node(index).item;    }    /**     * 更新指定位置的元素     */    public E set(int index, E element) {        checkElementIndex(index);        Node<E> x = node(index);        E oldVal = x.item;        x.item = element;        return oldVal;    }    /**     * 插入新元素到指定位置     */    public void add(int index, E element) {        // 检查插入位置是否合法        checkPositionIndex(index);        // 如果是尾部,则直接链接到尾部,否则先查找到指定为的元素,然后在其前链接元素。        if (index == size)            linkLast(element);        else            linkBefore(element, node(index));    }    /**     * 获取指定索引的元素     */    Node<E> node(int 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;        }    }    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的Object[]数组        public Object[] toArray() {        Object[] result = new Object[size];        int i = 0;        for (Node<E> x = first; x != null; x = x.next)            result[i++] = x.item;        return result;    }    // 返回LinkedList的模板数组。所谓模板数组,即可以将T设为任意的数据类型    public <T> T[] toArray(T[] a) {        if (a.length < size)            a = (T[])java.lang.reflect.Array.newInstance(                    a.getClass().getComponentType(), size);        int i = 0;        Object[] result = a;        for (Node<E> x = first; x != null; x = x.next)            result[i++] = x.item;        if (a.length > size)            a[size] = null;        return a;    }    private static final long serialVersionUID = 876323262645176354L;    // Positional Access Operations 随机访问位置的方法    // set     设置index位置对应的节点的值为element      // get     返回LinkedList指定位置的元素        // remove  删除index位置的节点     // Search Operations 搜索方法    // indexOf    // lastIndexOf    // Queue operations. 队列方法    // peek    返回第一个节点    // element 返回第一个节点     // pool    删除并返回第一个节点    // remove  删除并返回第一个节点    // offer   添加指定元素到队列尾部    // Deque operations  双端队列方法    // offerFirst 添加元素到队列头部    // offerLast  添加元素到队列尾部    // peekFirst  返回队头元素    // peekLast   返回队尾元素    // pollFirst  返回并移除队头元素    // pollLast   返回并移除队尾元素    // push       添加元素到链表头部     // pop        移除链表头部的数据    // 从LinkedList开始向后查找,删除第一个值为元素(o)的节点     // 从链表开始查找,如存在节点的值为元素(o)的节点,则删除该节点        // removeFirstOccurrence      // 从LinkedList末尾向前查找,删除第一个值为元素(o)的节点     // 从链表开始查找,如存在节点的值为元素(o)的节点,则删除该节点    // removeLastOccurrence    // 返回“index到末尾的全部节点”对应的ListIterator对象(List迭代器)    // listIterator    // List迭代器    // class ListItr}

关于LinkedList的源码,给出几点比较重要的总结:

1, 从源码中可以看出,LinkedList的实现是基于双向链表的,始终有头指针指向第一个结点,有尾指针指向最后一个结点。

2,注意两个不同的构造方法。无参构造方法中没有做任何事情,包含Collection的构造方法,将Collection中的数据加入到链表的尾部后面。

3,在查找和删除某元素时,源码中都划分为该元素为null和不为null两种情况来处理,LinkedList中允许元素为null。

4,LinkedList是基于链表实现的,因此不存在容量不足的问题,所以这里没有扩容的方法。

5,源码中node(int index)方法,该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中,我们看到这里有一个加速动作。源码中先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历,从而提高一定的效率(实际上效率还是很低)。

6,LinkedList是基于链表实现的,因此插入删除效率高,查找效率低(虽然有一个加速动作)。

7,在源码中提供了栈,队列,双端队列的操作方法,因此可以作为栈和双端队列使用。

0 0