LinkedList深入解析源码

1.add()

  public boolean add(E e) {
        //调用linkLast
        linkLast(e);
        //如果前面调用没有异常,返回true
        return true;
    }

    /**
     * 添加到链表尾部方法
     * @param e 数据
     */
    void linkLast(E e) {
        //定义l用于存储当前尾部的链表,方便后面连接
        final LinkedList.Node<E> l = last;
        //把数据e初始化为链表节点,并且节点头连接上last
        final LinkedList.Node<E> newNode = new LinkedList.Node<>(l, e, null);
        //刷新尾节点为新加的节点newNode,因为尾节点始终都要保存,方便以后从尾节点的遍历和存储及移除
        last = newNode;
        //如果尾节点为空也就是链为空没有数据,则首节点也为新节点newNode
        if (l == null)
            first = newNode;
            //否则代表链表有数据,则将链表的下一个节点连接上新节点newNode
        else
            l.next = newNode;
        //添加了一个新数据,数量加1
        size++;
        //修改次数加1,这个就统计修改次数,无关
        modCount++;
    }

    /**
     * 链表结构node
     * @param <E>
     */
    private static class Node<E> {
        //存储我们写入的数据
        E item;
        //链表的尾节点
        LinkedList.Node<E> next;
        //链表的头节点
        LinkedList.Node<E> prev;
        //链表的构造器主要是用于连接头尾节点,及存储元素
        Node(LinkedList.Node<E> prev, E element, LinkedList.Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    /**
     *
     * @param index 添加的索引位置
     * @param element 添加的元素
     */
    public void add(int index, E element) {
        //检查下标是是否越界
        checkPositionIndex(index);
        //如果index刚好等于链表长度也就是刚好要往最后一个里插入,则往最后插入
        if (index == size)
            //这个方法上面写了
            linkLast(element);
        //否则就是往中间和头部插入
        else
            //找到对应的节点,然后插入
            linkBefore(element, node(index));
    }

    private void checkPositionIndex(int index) {
        //如果越界抛出异常
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private boolean isPositionIndex(int index) {
        //当下标是否大于等于0且下于等于链表的长度的时候为true,否则false
        return index >= 0 && index <= size;
    }

    /**
     * node查询方法(采用二分查找的方法),节省时间
     * @param index
     * @return
     */
    LinkedList.Node<E> node(int index) {
        //如果下标小于长度/2,从头部循环一直找,找到后返回节点
        if (index < (size >> 1)) {
            LinkedList.Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
        //如果下标大于长度/2,从尾部循环一直找,找到后返回节点
            LinkedList.Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

    /**
     * 中间插入方法
     * @param e 元素
     * @param succ 对应下标的节点
     */
    void linkBefore(E e, LinkedList.Node<E> succ) {
        //用于存储链表的节点头部以上的链表数据
        final LinkedList.Node<E> pred = succ.prev;
        //把数据e初始化为链表节点,并且节点头连接上pred,节点尾连接上succ
        final LinkedList.Node<E> newNode = new LinkedList.Node<>(pred, e, succ);
        //把头部的链表连接上该节点,此时newNode<-node<-node<-node
        succ.prev = newNode;
        //如果链表头部节点为空,头部节点first指向newNode,目的是刷新头部节点,方便节点以后遍历、存储及移除
        if (pred == null)
            first = newNode;
        else
        //否则,把头部链表以上的数据连接上newNode,此时node->node->node->newNode
            pred.next = newNode;
        //添加了一个节点,数量加1
        size++;
        //修改次数加1,这个就统计修改次数,无关
        modCount++;
    }

总结:链表是双向链表,添加时需要进行指针改变,判断该往哪里插入,每次的头尾操作都会更新first、last这两节点,方便以后遍历、存储、移除操作。代码比较容易读懂,细细看就会发现其中的奥妙。

2.addAll()

 public boolean addAll(Collection<? extends E> c) {
        //调用了addAll方法,size:数据长度, c:集合数据
        return addAll(size, c);
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        //检查是否下标越界,用于往指定下标添加
        checkPositionIndex(index);
        //把集合转化成数组
        Object[] a = c.toArray();
        //获取数组的长度
        int numNew = a.length;
        //如果长度为0表明没有数据,返回false
        if (numNew == 0)
            return false;
        //pred用于存储节点以上链表,succ用于存储节点以下链表
        LinkedList.Node<E> pred, succ;
        //如果index==size代表往最后面加数据,此时存储节点以上链表pred直接指向最后节点,succ存储节点以下链表为空
        if (index == size) {
            succ = null;
            pred = last;
        } else {
        //否则存储节点以上链表pred为当前index节点的头部,存储节点以下链表succ为当前节点
            succ = node(index);
            pred = succ.prev;
        }

        //遍历数组
        for (Object o : a) {
            //转化成对应类并忽略检查警告
            @SuppressWarnings("unchecked") E e = (E) o;
            //把数据e初始化为链表节点并头部连接上节点以上链表pred
            LinkedList.Node<E> newNode = new LinkedList.Node<>(pred, e, null);
            //节点以上链表pred为空直接让首节点为newNode
            if (pred == null)
                first = newNode;
            else
            //否则往节点以上链表pred的后面连接上数据newNode
                pred.next = newNode;
            //节点以上链表pred重置该节点为newNode,方便循环后面连接
            pred = newNode;
        }
        //如果succ==null代表往链表的最后面加数据,此时尾节点last刚好等于循环完的pred
        if (succ == null) {
            last = pred;
        } else {
        //如果是往中间插入数据,此时需要把节点以上的数据pred:node->node->newNode->链表之前的节点succ
            pred.next = succ;
        //succ的头部要指向newNode,succ:node->node->pred
            succ.prev = pred;
        }
        //数量加上集合的长度
        size += numNew;
        //修改次数,无关
        modCount++;
        //上面无误,返回true
        return true;
    }

代码比较容易读懂,细看吧。

3.get()

public E get(int index) {
        //检查是否下标越界index>=0 && index<size
        checkElementIndex(index);
        //查询对应下标的index节点并返回.node()上面有写
        return node(index).item;
    }

4.remove()

  public E remove() {
        //调用removeFirst,不加参数默认移除前面的节点
        return removeFirst();
    }

    public E removeFirst() {
        //赋值f等于first首节点
        final LinkedList.Node<E> f = first;
        //如果首节点为空,抛出异常
        if (f == null)
            throw new NoSuchElementException();
        //解链表并移除节点,返回移除的节点
        return unlinkFirst(f);
    }

    private E unlinkFirst(LinkedList.Node<E> f) {
        //保存移除节点的值
        final E element = f.item;
        //移除节点后的新链表等于f.next
        final LinkedList.Node<E> next = f.next;
        //把原来节点的值置为空
        f.item = null;
        //连接也置为空
        f.next = null; // help GC
        //让首节点为next
        first = next;
        //如果移除节点后没有节点了也就是next为空了,需要把尾节点也置为空
        if (next == null)
            last = null;
        else
        //否则把next链表的头连接置为空,这样next就是头节点
            next.prev = null;
        //数量减1
        size--;
        //修改数加1
        modCount++;
        //返回移除的节点值
        return element;
    }

    //根据下标移除
    public E remove(int index) {
        //检查下标是否越界,越界抛出异常
        checkElementIndex(index);
        //找到节点并进行移除
        return unlink(node(index));
    }


    E unlink(LinkedList.Node<E> x) {
        //保存移除的值
        final E element = x.item;
        //移除节点的下一个节点
        final LinkedList.Node<E> next = x.next;
        //移除节点的上一个节点,这两节点连接起来就移除了对应节点
        final LinkedList.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;
        //数量减1
        size--;
        //修改数加1
        modCount++;
        //返回移除节点的值
        return element;
    }

    //根据值移除
    public boolean remove(Object o) {
        //如果值为空
        if (o == null) {
            //从链表的头部节点开始找,一直循环
            for (LinkedList.Node<E> x = first; x != null; x = x.next) {
                //当找到节点值为空时
                if (x.item == null) {
                    //进行移除,这个方法上面有写
                    unlink(x);
                    return true;
                }
            }
        } else {
            //从链表的头部节点开始找,一直循环
            for (LinkedList.Node<E> x = first; x != null; x = x.next) {
                //当找到节点值等于要移除值时
                if (o.equals(x.item)) {
                    //进行移除,这个方法上面有写
                    unlink(x);
                    return true;
                }
            }
        }
        //如果没有找到对应的值返回false
        return false;
    }

代码比较简单,慢慢看!

总结一下:总体来说LinkedList代码比较简单,只要知道linkedlist是双向链表结构,脑子有这个图形,就不难读懂。其中涉及到指针的修改,解链、连接链等也是比较容易读懂,干就完了,奥利给!

猜你喜欢

转载自blog.csdn.net/ysfengshu/article/details/127012904