LinkedList源码分析

概述

1 LinkedList是List接口的双向链表非同步实现,并允许包括null在内的所有元素。
  2 底层的数据结构是基于双向链表的,该数据结构我们称为节点。它也可以被当作堆栈、队列(实现 了List 接口)或双端队列(实现 了Deque 接口)进行操作。
  3 双向链表节点对应的类Node的实例,Node中包含成员变量:prev,next,item。其中,prev是该节点的上一个节点,next是该节点的下一个节点,item是该节点所包含的值。
  4 它的查找是分两半查找,先判断index是在链表的哪一半,然后再去对应区域查找,这样最多只要遍历链表的一半节点即可找到

  优化:结点在前半段则从头开始遍历,后半段则从尾开始遍历,专业爆炸遍历最多一般结点就可以找到结点。

源码分析

继承结构

属性

{
    // 实际元素个数
    transient int size = 0;
    // 头结点
    transient Node<E> first;
    // 尾结点
    transient Node<E> last;
}

构造方法

1)无参构造函数
    public LinkedList() {
    }
2)有参构造函数
public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
}

内部类

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

 public boolean add(E e) {
          // 添加到末尾
          linkLast(e);
          return true;
      }

    void linkLast(E e) {
        final Node<E> l = last;    //临时节点l(L的小写)保存last,也就是l指向了最后一个节点
        final Node<E> newNode = new Node<>(l, e, null);//将e封装为节点,并且e.prev指向了最后一个节点
        last = newNode;  //newNode成为了最后一个节点,所以last指向了它
        if (l == null)    //判断是不是一开始链表中就什么都没有,如果没有,则newNode就成为了第一个节点,first和last都要指向它
            first = newNode;
        else    //正常的在最后一个节点后追加,那么原先的最后一个节点的next就要指向现在真正的最后一个节点,原先的最后一个节点就变成了倒数第二个节点
            l.next = newNode;
        size++;//添加一个节点,size自增
        modCount++;
    }

remove

//首先通过看上面的注释,我们可以知道,如果我们要移除的值在链表中存在多个一样的值,那么我们会移除index最小的那个,也就是最先找到的那个值,如果不存在这个值,那么什么也不做
    public boolean remove(Object o) {
//这里可以看到,linkedList也能存储null
        if (o == null) {
//循环遍历链表,直到找到null值,然后使用unlink移除该值。下面的这个else中也一样
            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;
    }

//不能传一个null值过,注意,看之前要注意之前的next、prev这些都是谁。
    E unlink(Node<E> x) {
        // assert x != null;
//拿到节点x的三个属性
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

//这里开始往下就进行移除该元素之后的操作,也就是把指向哪个节点搞定。
        if (prev == null) {
//说明移除的节点是头节点,则first头节点应该指向下一个节点
            first = next;
        } else {
//不是头节点,prev.next=next:有1、2、3,将1.next指向3
            prev.next = next;
//然后解除x节点的前指向。
            x.prev = null;
        }

        if (next == null) {
//说明移除的节点是尾节点
            last = prev;
        } else {
//不是尾节点,有1、2、3,将3.prev指向1. 然后将2.next=解除指向。
            next.prev = prev;
            x.next = null;
        }
//x的前后指向都为null了,也把item为null,让gc回收它
        x.item = null;
        size--;    //移除一个节点,size自减
        modCount++;
        return element;    //由于一开始已经保存了x的值到element,所以返回。
    }

get(index)

 public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

indexOf(Object o)

//这个很简单,就是通过实体元素来查找到该元素在链表中的位置。跟remove中的代码类似,只是返回类型不一样。
   public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

 

迭代器

ListItr 

  不止有向后迭代的方法,还有向前迭代的方法,所以我们就知道了这个ListItr这个内部类干嘛用的了,就是能让linkedList不光能像后迭代,也能向前迭代。
  看一下ListItr中的方法,可以发现,在迭代的过程中,还能移除、修改、添加值得操作。

posted @ 2019-08-27 18:34  南山的海风  阅读(127)  评论(0编辑  收藏  举报