JAVA集合源码解析 LinedList 探索(基于JDK1.8)

LinedList

本次对LinedList的分析是基于JDK1.8来进行的

按照目录的结构来进行分析


1.LinedList简介

这里写图片描述

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

易知LinkedList 继承了AbstractSequentialList双向链表,所以他可以用作栈,队列或双端队列;
还实现了List接口,是有序的列表;
实现了Deque,支持两端的元素插入和移除;
实现了Cloneable接口,能够使用clone()方法;
实现了Serializable接口,支持序列化操作

ArrayList在执行随机访问很快速,但是在执行插入或删除效率略低,LinkedList和ArrayList一样都基本实现的了List接口,但是作为双端队列,在插入和删除操作时比ArrayList更高效.

2.1属性

   //实际的元素个数
    transient int size = 0;

    /**
     * 头节点
     */
    transient Node<E> first;

    /**
     * 尾节点
     */
    transient Node<E> last;

LinkedList对size,头结点,尾结点进行transient关键字修饰,使其不能被序列化

2.2构造函数

    /**
     * 空构造函数
     */
    public LinkedList() {
    }

    /**
     * 带参数的构造函数
     * @param  c 放入此列表中的集合
     */
    public LinkedList(Collection<? extends E> c) {
        //调用无参构造函数
        this();
        //添加集合中所有元素
        addAll(c);
    }

addAll(c)中又调用另一个重载的addAll(size, c);方法

public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

最终的addAll(size, c)方法如下:

    /**
     * 将指定集合中的所有元素插入到此列表,从指定的位置开始。
     * 将当前在该位置的元素(如果有的话)和随后的元素移到右侧(增加它们的索引)。
     * 新元素将按照它们由指定集合的迭代器返回的顺序出现在列表中。
     *
     * @param index 在哪个索引处插入指定集合中的第一个元素
     * @param c 包含要添加到此列表中的元素的集合
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        //检查传入的索引位置是否有效
        checkPositionIndex(index);
        //将目标集合转换保存为a对象数组
        Object[] a = c.toArray();
        //获取插入目标的长度
        int numNew = a.length;
        //如果新数组长度为0
        if (numNew == 0)
            //返回false
            return false;

        Node<E> pred, succ;//前驱,后继
        if (index == size) {//如果插入位置为末尾
            succ = null;//后继置空
            pred = last;//前驱更新为尾结点
        } else {//如果不为末尾
            succ = node(index);//获取索引位置的结点
            pred = succ.prev;//保存该结点的前驱
        }

        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            //新建结点,前驱为pred,元素为e,后继为空
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)   //如果前驱为空
                first = newNode;//重新赋值头结点
            else    //如果前驱不为空
                pred.next = newNode;//赋值新结点为pred的后继
            pred = newNode;//重新赋值pred结点
        }

        if (succ == null) {//如果succ为空
            last = pred;//重新赋值尾结点
        } else {    //如果succ不为空
            pred.next = succ;//赋值pred的后继为succ
            succ.prev = pred;//赋值succ的前驱为pred
        }
        //链表长度加上插入的长度
        size += numNew;
        //结构性加1
        modCount++;
        return true;
    }

2.3核心方法

增加方法

add(E e)

    /**
     * 增加一个元素到尾部,成功返回true
     *
     * @param e 
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

调用linkLast(E e)方法,添加目标元素到尾结点

    /**
     * 添加一个元素到尾结点
     */
    void linkLast(E e) {
        //保存尾结点为final不可变类型
        final Node<E> l = last;
        //初始化结点对象,前驱为l,数据域为元素e,后继为null
        final Node<E> newNode = new Node<>(l, e, null);
        //重新赋值尾结点
        last = newNode;
        if (l == null)  //尾结点为空
            first = newNode;//赋值新结点为头结点
        else    //尾结点不为空
            l.next = newNode;//尾结点的后继赋值为新结点
        //结点大小加1
        size++;
        //结构性加1
        modCount++;
    }

移除方法

remove(Object o)其实就是删除索引最小的元素(如果有多个重复值的话)

    /**
     * 从此链表中删除第一次出现的指定元素,
     * 如果不包含该元素,则不变。
     *
     * @param o
     */
    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;//返回true
                }
            }
        }
        return false;//不包含返回false
    }

调用unlink(Node x)来移除结点

    /**
     * 移除非空结点
     */
    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) {//如果prev结点为空
            first = next;   //重新赋值头结点
        } else {    //如果非空
            prev.next = next;   //重新赋值prev的尾结点
            x.prev = null;  //目标结点前驱置空
        }

        if (next == null) {//如果next结点为空
            last = prev;    //重新赋值尾结点
        } else {    //如果非空
            next.prev = prev;   //重新赋值尾结点的前驱
            x.next = null;  //置空目标结点后继
        }
        //置空目标元素
        x.item = null;
        //结点大小减1
        size--;
        //结构性加1
        modCount++;
        //返回移除的元素值
        return element;
    }

查找方法

get(int index)

    /**
     * 返回此列表中指定位置的元素
     *
     * @param index 要返回的元素的索引
     * @return 该列表中指定位置的元素
     */
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

调用node(int index)方法返回指定元素索引处的(非空)节点值。

    /**
     * 返回指定元素索引处的(非空)节点。
     */
    Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {//如果索引位置小于总长度除以2
            Node<E> x = first;  //保存头结点
            for (int i = 0; i < index; i++)//从头结点开始遍历直到索引位置获取相关结点
                x = x.next;
            return x;//返回结点
        } else {//如果索引位置大于总长度除以2
            Node<E> x = last;//保存尾结点
            for (int i = size - 1; i > index; i--)//从尾结点开始往前遍历
                x = x.prev;//获取相关结点
            return x;//返回结点
        }
    }

更新方法

set(int index, E element)

    /**
     * 用指定的元素替换此列表中指定位置的元素
     *
     * @param index 要替换的元素的索引
     * @param element 要存储在指定位置的元素
     * @return 之前在指定位置的元素
     */
    public E set(int index, E element) {
        checkElementIndex(index);//检查传入的位置是否有效
        Node<E> x = node(index);//保存查询到的位置结点
        E oldVal = x.item;//保存指定位置的旧值
        x.item = element;//把新值赋值给该位置结点
        return oldVal;
    }

总结

在我们需要进行快速的从列表的端点插入或者移除元素时,LinedList只需要链接新的元素,而不必修改列表中剩余元素,我们便可以使用它,如果要执行大量的随机访问,LinedList的访问时间将随列表的大小而明显增加,不适用随机访问的场景.它不是同步的

猜你喜欢

转载自blog.csdn.net/ouzhuangzhuang/article/details/80257592