LinkedList源码分析

Node

Node是LinkedList存储的基本单位

    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;
        }
    }

add方法

源码

  1. 保存之前的尾节点
  2. 创建新的节点,新节点的前置直接点是之前的尾节点,后置节点为null
  3. 将last指向最新的尾节点
  4. 如果之前的链表中一个元素也没有,那么新创建的节点既是尾节点又是头结点
  5. 如果之前链表中已经有元素,那么将之前的尾节点指向新的节点
  6. 将数量加一
    /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        // 创建节点,指向前一个元素,后置节点为null,本身自己作为最后一个元素
        final Node<E> newNode = new Node<>(l, e, null);
        // 将last指向最新的尾节点
        last = newNode;
        if (l == null) // e加入之前,链表中一个元素也没有
            first = newNode;
        else // 将之前的尾节点的next指向新的尾节点
            l.next = newNode;
        // 数量加1
        size++;
        modCount++;
    }

用图像来表示加入的过程

add(int index, E element)

源码

  1. 查找链表中下标为index 的节点,利用二分法查找,先判断index是在左边还是右边,如果是左边,就从做往右查找,如果是右边,就从右往左查找,假设找到的节点为A。
  2. 如果是在尾部插入,就调用linkLast方法,其实现和add(E e)方法一样
  3. 如果是在中间插入,就创建新的节点,假设新的节点为B,B的后置节点为A,前置节点为A的前置节点,若A的前置节点为null(在链表头插入),就将first指向新的节点B,若A的前置节点C不为null,那么就将前置节点C的next指向新节点B。
  4. 将数量加1
   // 父类中的方法
   public void add(int index, E element) {
        try {
            listIterator(index).add(element);
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }


    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

   private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;

        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }
    }

    // 返回下标为index的元素
    Node<E> node(int index) {
        // assert isElementIndex(index);

         // 如果index在正中间的左边,那么就从做开始遍历
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            // 如果index靠右,就从右向左遍历
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

       // ListItr 中的方法
        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null) // 在尾部插入
                linkLast(e);
            else // 在中间插入
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }
    // 和add(E e)的实现是相同的
    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++;
    }

    /**
     * Inserts element e before non-null Node succ.
     */
    void linkBefore(E e, Node<E> succ) { // succ为之前下标为index的节点
        // assert succ != null;
       // succ节点的前置节点
        final Node<E> pred = succ.prev;
        // 创建新的节点,前直接点为pred,后置节点为succ
        final Node<E> newNode = new Node<>(pred, e, succ);
        // 将succ的前置节点指向新的节点
        succ.prev = newNode;
        if (pred == null)// 如果实在头部插入,那么新的节点就是头结点
            first = newNode;
        else// 将pred的next指向新的节点
            pred.next = newNode;
        // 将数量加1
        size++;
        modCount++;
    }

添加节点的流程图

 

删除元素

remove()

调用这个方法,删除的是头结点。

    /**
     * Retrieves and removes the head (first element) of this list.
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */
    public E remove() {
        return removeFirst();
    }

    /**
     * Removes and returns the first element from this list.
     *
     * @return the first element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeFirst() {
        final Node<E> f = first;
        // 链表中没有元素,删除元素,会抛出异常
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    /**
     * Unlinks non-null first node f.
     */
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        // 头结点的后置节点
        final Node<E> next = f.next;
        // 将item置为null,帮助GC垃圾回收
        f.item = null;
        f.next = null; // help GC
        // 之前链表的第二个节点就成为了头节点
        first = next;
        // 如果删除了节点之后,链表中没有节点了,尾节点需要置为null
        if (next == null)
            last = null;
        else // 将当前头结点的前置节点置为null
            next.prev = null;
        // 数量减一
        size--;
        modCount++;
        return element;
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值