LinkedList source code analysis --jdk1.8

LinkedList Overview

  1.LinkedList is implemented with a set of doubly-linked list, based on the set internal class Node <E> achieved.
  2.LinkedList support doubly linked list access, cloning, sequencing, and ordered the element can be repeated.
  3.LinkedList size is not initialized, and no expansion mechanism, through the head node, tail node iterative search.

LinkedList data structure

  The data structure is the essence of a collection of lies, data structures often limit the role and focus of the collection, understand the various data structures we analyze the source code is the only way.
  LinkedList data structure follows:
LinkedList source code analysis --jdk1.8
list Basics added:
1) singly linked list:
      Element: element used to store
      next: a node is used to point to the next element
      through a pointer pointing to each node so that the next node linked structure, the last node of the next point to null.
LinkedList source code analysis --jdk1.8
2) one-way circular linked list
      Element, with the next as before
      the next node will be the last way linked list node points to the head, rather than to null, so that deposit into a ring
LinkedList source code analysis --jdk1.8
3) doubly linked list
      element: storage element
      pre: used to point a front element
      next: point to an element after
      doubly linked list comprising two pointers, a node pre front point, a point after the next node, but the head of the first node pointing pre null, the last node of the tail pointing null.
LinkedList source code analysis --jdk1.8
4) Bidirectional circular linked list
      Element, as pre, with the front of the next
      pre first node to the last node, the last node of the next points to the first node, but also to form a "ring."
LinkedList source code analysis --jdk1.8

LinkedList source code analysis

/**
 * LinkedList 使用 iterator迭代器更加 快速
 * 用链表实现的集合,元素有序且可以重复
 * 双向链表
 */
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    /**
     * 实际元素个数
     */
    transient int size = 0;
    /**
     * 头结点
     */
    transient Node<E> first;
    /**
     * 尾结点
     */
    transient Node<E> last;
    /**
     * 无参构造方法.
     */
    public LinkedList() {
    }
    /**
     * 集合参数构造方法
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
         /**
     * 内部类Node
     */
         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 implement inheritance and analysis

LinkedList source code analysis --jdk1.8
   LinkedList extends AbstractSequentialList<E>
   AbstractSequentialList extends AbstractList
   AbstractList extends AbstractCollection
  java中所有类都继承Object,所以LinkedList的继承结构如上图。
   1. AbstractSequentialList是一个抽象类,继承了AbstractList接口,AbstractList抽象类中可以有抽象方法,还可以有具体的实现方法,AbstractList实现接口中一些通用的方法,AbstractSequentialList再继承AbstractList,拿到通用基础的方法,然后自己在重写实现基于链表的方法:add/addAll/get/iterator/listIterator/remove/set,这样的好处是:让代码更简洁,AbstractList随机存取功能基类,AbstractSequentialList链表存取功能基类,父类抽象,子类个性,父类一般是抽象类,由子类来实现丰富。
   2.LinkedList实现了List<E>、Deque<E>、Cloneable、Serializable接口。
     1)List<E>接口,集合通用操作方法定义。
     2)Deque<E>接口,双向队列,在Queue单项队列的基础上增加为双向队列,提高查询/操作效率
     3)Cloneable接口,可以使用Object.Clone()方法。
     4)Serializable接口,序列化接口,表明该类可以被序列化,什么是序列化?简单的说,就是能够从类变成字节流传输,反序列化,就是从字节流变成原来的类

LinkedList核心方法分析

1. add方法(6种重载实现)--增    

LinkedList source code analysis --jdk1.8

     1)add(E);//默认直接在末尾添加元素

/**
 * 新增元素
 */
 public boolean add(E e) {
            // 添加到末尾
                linkLast(e);
                return true;
        }
    /**
 * 链接到末尾.
 */
void linkLast(E e) {
    // 保存尾结点,l为final类型,不可更改
    final Node<E> l = last;
    // 新生成结点的上一个为l,下一个为null
    final Node<E> newNode = new Node<>(l, e, null);
    // 重新赋值尾结点
    last = newNode;
    if (l == null) // 尾结点为空
        first = newNode; // 赋值头结点
    else
        l.next = newNode; // 尾结点的下一个为新生成的结点
    size++; // 大小加1    
    modCount++; // 结构性修改加1
}

     2)add(int index, E element);//给指定下标,添加元素

/**
 * 在index位置插入节点
 * 1.如果index等于size,则在末尾新增元素,原因:size为实际元素个数,index为下标,所以index=size时,说明要在末尾插入元素
 * 2.如果index不等于size,则根据index下标找到节点,在节点前插入元素,原因:需要占用index下标位置。
 */
public void add(int index, E element) {
            //查看下标是否越界
    checkPositionIndex(index);
    //如果指定下标等于实际元素个数,则添加到末尾
    if (index == size)
        linkLast(element);
    else //否则,找到index位置元素添加到index后
        linkBefore(element, node(index));
}

/**

  • 判断下标是否越界
    */
    private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    /**
  • 根据index下标找到节点
  • 优化:由于是双向链表,所以判断索引位置(size/2),前半段从头节点开始查找,后半段从尾节点开始查找
    */
    Node<E> node(int index) {
    // assert isElementIndex(index);
    // 判断插入的位置在链表前半段或者是后半段 size/2的1次方
    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;
    }
    }
    /**
  • 在非空节点succ前插入数据
    */
    void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    final Node<E> pred = succ.prev;
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)
    first = newNode;
    else
    pred.next = newNode;
    size++;
    modCount++;
    }

         3)addAll(Collection<? extends E> c);//添加Collection类型元素

    /**

  • 添加一个集合
    */
    public boolean addAll(Collection<? extends E> c) {
    //在末尾添加
    return addAll(size, c);
    }

     4)addAll(int index, Collection<? extends E> c);//指定位置,添加Collection类型元素

/**

  • 从指定的位置开始,将指定collection中的所有元素插入到此列表中,新元素的顺序为指定collection的迭代器所返回的元素顺序
    */
    public boolean addAll(int index, Collection<? extends E> c) {
    // 检查插入的的位置是否合法
    checkPositionIndex(index);
    // 将集合转化为数组
    Object[] a = c.toArray();
    // 保存集合大小
    int numNew = a.length;
    if (numNew == 0) // 集合为空,直接返回
    return false;
    Node<E> pred, succ; //上一个 下一个
    if (index == size) { // 如果插入位置为链表末尾,则后继为null,上一个为尾结点
    succ = null;
    pred = last;
    } else { // 插入位置为其他某个位置
    succ = node(index); // 寻找到该结点
    pred = succ.prev; // 保存该结点的上一个
    }
    for (Object o : a) { // 遍历数组
    @SuppressWarnings("unchecked") E e = (E) o;
    Node<E> newNode = new Node<>(pred, e, null); // 生成新结点
    if (pred == null) // 表示在第一个元素之前插入(索引为0的结点)
    first = newNode;
    else
    pred.next = newNode;
    pred = newNode;
    }
    if (succ == null) { // 表示在最后一个元素之后插入
    last = pred;
    } else {
    pred.next = succ;
    succ.prev = pred;
    }
    // 修改实际元素个数
    size += numNew;
    // 结构性修改加1
    modCount++;
    return true;
    }

     5)addFirst(E e);//头结点添加元素

/**
 * 头结点插入元素
 */
public void addFirst(E e) {
    linkFirst(e);
}
     /**
 * 链接头结点
 */
private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);//新建节点,头结点为null,尾节点为first
    first = newNode;
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

     5)addLast(E e);//尾结点添加元素

 /**
 * 尾节点添加元素
 */
public void addLast(E e) {
    linkLast(e);
}
     /**
 * 链接尾节点
 */
void linkLast(E e) {
    // 保存尾结点,l为final类型,不可更改
    final Node<E> l = last;
    // 新生成结点的上一个为l,下一个为null
    final Node<E> newNode = new Node<>(l, e, null);
    // 重新赋值尾结点
    last = newNode;
    if (l == null) // 尾结点为空
        first = newNode; // 赋值头结点
    else
        l.next = newNode; // 尾结点的下一个为新生成的结点
    size++; // 大小加1    
    modCount++; // 结构性修改加1
}

   

2.remove方法(7种重载实现)--删

LinkedList source code analysis --jdk1.8

     1)remove(int index); //根据指定下标 删除元素     

    /**
 * 根据指定下标 删除元素
 */
public E remove(int index) {
    //判断索引是否越界
    rangeCheck(index);
    modCount++;
    //获取旧元素
    E oldValue = elementData(index);
    //将数组elementData中index位置之后的所有元素向前移一位
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //将原数组最后一个位置置为null,由GC清理
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}       

     2)remove(Object o); //根据指定元素 删除元素 

 /**
 * 移除ArrayList中首次出现的指定元素(如果存在),ArrayList中允许存放重复的元素
 */
public boolean remove(Object o) {
    // 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                //私有的移除方法,跳过index参数的边界检查以及不返回任何值
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}   
    /*
 * 根据下标快速删除元素
 */
private void fastRemove(int index) {
    modCount++;
    //将数组elementData中index位置之后的所有元素向前移一位
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}
     /**
 * 清空ArrayList,将全部的元素设为null,等待垃圾回收将这个给回收掉,所以叫clear
 */
public void clear() {
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}

     3)removeAll(Collection<?> c); //删除包含在指定容器c中的所有元素 

/**
 * 删除ArrayList中包含在指定容器c中的所有元素
 */
public boolean removeAll(Collection<?> c) {
    //检查指定的对象c是否为空
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}
     /**
 * 删除全部
* @author jiaxiaoxian
* @date 2019年2月12日 
 */
private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0; //读写双指针
    boolean modified = false;
    try {
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement) //判断指定容器c中是否含有elementData[r]元素
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

     4)removeIf(Predicate<? super E> filter); //按照一定规则过滤(删除)集合中的元素 

/**
 * 按照一定规则过滤(删除)集合中的元素
 * 如:idList.removeIf(id -> id == nul);
    *   去掉 List idList 集合中id 为 null 的
 * @param filter
 * @return
 */
@Override
public boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    // figure out which elements are to be removed
    // any exception thrown from the filter predicate at this stage
    // will leave the collection unmodified
    int removeCount = 0;
    final BitSet removeSet = new BitSet(size);
    final int expectedModCount = modCount;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        @SuppressWarnings("unchecked")
        final E element = (E) elementData[i];
        if (filter.test(element)) {
            removeSet.set(i);
            removeCount++;
        }
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }

    // shift surviving elements left over the spaces left by removed elements
    final boolean anyToRemove = removeCount > 0;
    if (anyToRemove) {
        final int newSize = size - removeCount;
        for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
            i = removeSet.nextClearBit(i);
            elementData[j] = elementData[i];
        }
        for (int k=newSize; k < size; k++) {
            elementData[k] = null;  // Let gc do its work
        }
        this.size = newSize;
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

    return anyToRemove;
}

总结:
   remove函数用户移除指定下标的元素,此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,会被GC,可以作为小的技巧使用。

3.set方法--改

/**
 * 覆盖指定下标元素
 */
    public E set(int index, E element) {
      //判断下标是否越界
    checkElementIndex(index);
             //获得下标节点
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
     /**
 * 判断下标是否越界
 */

private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

4.get方法--查

LinkedList source code analysis --jdk1.8
/**

Guess you like

Origin blog.51cto.com/hackerxian/2426839