LinkedList源码分析

Posted zitai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LinkedList源码分析相关的知识,希望对你有一定的参考价值。

简介

LinkedList是以双向链表为数据结构的容器。它可以进行堆栈、队列、双端队列的操作。

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

LinkedList 继承AbstractSequentialList,该被继承类是抽象类,是在迭代器的基础上实现的get、set、add和remove方法。该抽象类更多信息:AbstractSequentialList

LinkedList实现 Deque接口:能当做双端队列使用。

LinkedList实现 Cloneable接口:具有克隆功能。

LinkedList实现 Serializable接口:具有序列化功能。


API

boolean       add(E object)
void          add(int location, E object)
boolean       addAll(Collection<? extends E> collection)
boolean       addAll(int location, Collection<? extends E> collection)
void          addFirst(E object)
void          addLast(E object)
void          clear()
Object        clone()
boolean       contains(Object object)
Iterator<E>   descendingIterator()
E             element()
E             get(int location)
E             getFirst()
E             getLast()
int           indexOf(Object object)
int           lastIndexOf(Object object)
ListIterator<E>     listIterator(int location)
boolean       offer(E o)
boolean       offerFirst(E e)
boolean       offerLast(E e)
E             peek()
E             peekFirst()
E             peekLast()
E             poll()
E             pollFirst()
E             pollLast()
E             pop()
void          push(E e)
E             remove()
E             remove(int location)
boolean       remove(Object object)
E             removeFirst()
boolean       removeFirstOccurrence(Object o)
E             removeLast()
boolean       removeLastOccurrence(Object o)
E             set(int location, E object)
int           size()
<T> T[]       toArray(T[] contents)
Object[]     toArray()

属性

//链表节点
transient int size = 0;
//双向链表表头节点
transient Node<E> first;
//双向链表表尾节点
transient Node<E> last;
//序列号
private static final long serialVersionUID = 876323262645176354L;

Node节点

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

特别说明

/**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

由注释内容知道,当双链表为空的时候,first和last都为空;当双链表都不为空的时候,first的前节点指针为空,first的数据不为空,last的后节点指针为空,last的数据不为空。


构造函数

//创建一个空的链表
public LinkedList() {
}

//将其他容器数据放入初始化链表中
public LinkedList(Collection<? extends E> c) {
      this();
      addAll(c);
}

//在first节点前面添加节点
private void linkFirst(E e) {
      //保存first节点
      final Node<E> f = first;
      //创建节点,该节点前节点为null,后节点为first
      final Node<E> newNode = new Node<>(null, e, f);
      //更换first节点
      first = newNode;
      //若该链表为空,则也把last更换为新节点
      if (f == null)
        last = newNode;
      else
      //若该链表非空,则将原本first的前指针指向新的的first
        f.prev = newNode;
      //节点数++
      size++;
      modCount++;
}

//在last节点后面添加节点
void linkLast(E e) {
      //保存last节点
      final Node<E> l = last;
      //创建节点,该节点前节点为last,后节点为null
      final Node<E> newNode = new Node<>(l, e, null);
      //更换last节点
      last = newNode;
      //若该链表为空,则也把first更换为新节点
      if (l == null)
        first = newNode;
      else
      //若该链表非空,则将原本last的后指针指向新的的last
        l.next = newNode;
      //节点数++
      size++;
      modCount++;
}

//在指定节点前添加一个新节点
void linkBefore(E e, Node<E> succ) {
       	//获取指定节点的前节点pre
        final Node<E> pred = succ.prev;
  			//创建节点,该节点前节点为pre,后节点为指定节点succ
        final Node<E> newNode = new Node<>(pred, e, succ);
  			//更改指定节点前指针,为e
        succ.prev = newNode;
  			//若succ为first或者改链表节点数为1时,修改first节点
        if (pred == null)
            first = newNode;
        else
        //更改前节点pre的后指针为新节点newNode
            pred.next = newNode;
        size++;
        modCount++;
}

//移除first节点
private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
  			//前提就是f为first且不为空
  
  			//获取first节点数据
        final E element = f.item;
  			//获取first节点指向的下一个节点
        final Node<E> next = f.next;
  			//将first节点置空
        f.item = null;
        f.next = null; // help GC
  
  			//first赋值为下一个节点next
        first = next;
  			
  			//若next为空,即链表长度为1,为将last置空
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
}

//移除last节点
private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
  			//前提就是l为last且不为空
  
        final E element = l.item;
  			//获取last节点指向的前一个节点
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
  
  			//last赋值为前一个节点prev
        last = prev;
  			
  			//若prev为空,即链表长度为1,为将first置空
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
}

//移除节点
E unlink(Node<E> x) {
   		//获取该移除节点数据、前节点、后节点
      final E element = x.item;
      final Node<E> next = x.next;
      final Node<E> prev = x.prev;

  		//若移除节点为first,修改first为后节点
      if (prev == null) {
        first = next;
      } else {
        //前节点与后节点互连
        prev.next = next;
        //将移除的节点的前指针置空
        x.prev = null;
      }
			
  		//若移除节点为last,则修改last为前节点
      if (next == null) {
        last = prev;
      } else {
        //前节点与后节点互连
        next.prev = prev;
        //将移除的节点的后指针置空
        x.next = null;
      }
      x.item = null;
      size--;
      modCount++;
      return element;
}

//获取first节点数据
public E getFirst() {
    final Node<E> f = first;
    if (f == null)
      throw new NoSuchElementException();
    return f.item;
}

//获取last节点数据
public E getLast() {
    final Node<E> l = last;
    if (l == null)
      throw new NoSuchElementException();
    return l.item;
}

//移除first节点
public E removeFirst() {
    final Node<E> f = first;
    if (f == null)
      throw new NoSuchElementException();
    return unlinkFirst(f);
}

//移除last节点
public E removeLast() {
    final Node<E> l = last;
    if (l == null)
      throw new NoSuchElementException();
    return unlinkLast(l);
}

//在链表头部添加数据
public void addFirst(E e) {
  	linkFirst(e);
}

//在链表尾部添加数据
public void addLast(E e) {
  	linkLast(e);
}

//检测是否存在数据o
public boolean contains(Object o) {
  	return indexOf(o) != -1;
}

//返回链表节点数
public int size() {
  	return size;
}

//往链表末尾添加数据
public boolean add(E e) {
    linkLast(e);
    return true;
}

//移除指定数据的节点
public boolean remove(Object o) {
  	//数据为null
    if (o == null) {
      //遍历链表
      for (Node<E> x = first; x != null; x = x.next) {
        if (x.item == null) {
          //调用移除节点方法
          unlink(x);
          return true;
        }
      }
    } else { //数据不为null
      //遍历链表
      for (Node<E> x = first; x != null; x = x.next) {
        if (o.equals(x.item)) {
          //调用移除节点方法
          unlink(x);
          return true;
        }
      }
    }
    return false;
}

//在链表尾部添加其他容器的数据
public boolean addAll(Collection<? extends E> c) {
  	return addAll(size, c);
}

//在指定位置添加其他容器的数据
public boolean addAll(int index, Collection<? extends E> c) {
  	//检测位置是否合法
    checkPositionIndex(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0)
      return false;
		
  	//pre为插入位置的前节点,succ为插入位置的后节点
    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;
      Node<E> newNode = new Node<>(pred, e, null);
      if (pred == null)
        first = newNode;
      else
        pred.next = newNode;
      pred = newNode;
    }
		
  	//若succ为null,则表示插入位置为size,需要重新对last赋值
    if (succ == null) {
      last = pred;
    } else {
      pred.next = succ;
      succ.prev = pred;
    }

    size += numNew;
    modCount++;
    return true;
}

//清空链表数据,其实就是遍历链表,置空属性
public void clear() {
    for (Node<E> x = first; x != null; ) {
      Node<E> next = x.next;
      x.item = null;
      x.next = null;
      x.prev = null;
      x = next;
    }
    first = last = null;
    size = 0;
    modCount++;
}

//根据位置获取节点
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

//根据位置更改该节点信息
public E set(int index, E element) {
    //判断位置是否合法
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

//根据位置添加数据
public void add(int index, E element) {
    checkPositionIndex(index);
		//若位置为链表尾部,直接调用linkLast
    if (index == size)
      linkLast(element);
    else
      linkBefore(element, node(index));
}

//根据位置移除节点
public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

//判断索引是否非法
private boolean isElementIndex(int index) {
  	return index >= 0 && index < size;
}
private boolean isPositionIndex(int index) {
  	return index >= 0 && index <= size;
}
private String outOfBoundsMsg(int index) {
  	return "Index: "+index+", Size: "+size;
}
private void checkElementIndex(int index) {
    if (!isElementIndex(index))
      throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void checkPositionIndex(int index) {
    if (!isPositionIndex(index))
      throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

//根据索引获取节点
Node<E> node(int index) {
		//当索引小于节点数一半,则从first节点向后开始寻找
    if (index < (size >> 1)) {
      Node<E> x = first;
      for (int i = 0; i < index; i++)
        x = x.next;
      return x;
    } else {
      //当索引大于等于节点数一半,则从last节点向前开始寻找
      Node<E> x = last;
      for (int i = size - 1; i > index; i--)
        x = x.prev;
      return x;
    }
}

//根据指定数据获取节点位置(正向)
public int indexOf(Object o) {
    int index = 0;
  	//通过遍历数据来寻找,数据照旧有两种情况,null和非null
    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;
}

//根据指定数据获取节点位置(反向)
public int lastIndexOf(Object o) {
    int index = size;
  //通过遍历数据来寻找,数据照旧有两种情况,null和非null
    if (o == null) {
      for (Node<E> x = last; x != null; x = x.prev) {
        index--;
        if (x.item == null)
          return index;
      }
    } else {
      for (Node<E> x = last; x != null; x = x.prev) {
        index--;
        if (o.equals(x.item))
          return index;
      }
    }
    return -1;
}

//获取first节点的数据
public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

//获取first节点的数据
public E element() {
  return getFirst();
}

//移除first节点
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

//移除first节点
public E remove() {
  	return removeFirst();
}

//在链表末尾添加数据
public boolean offer(E e) {
 		 return add(e);
}

//在链表头部添加数据
public boolean offerFirst(E e) {
  	addFirst(e);
    return true;
}

//在链表末尾添加数据
public boolean offerLast(E e) {
    addLast(e);
    return true;
}

//查看first节点数据
public E peekFirst() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

//查看last节点数据
public E peekLast() {
    final Node<E> l = last;
    return (l == null) ? null : l.item;
}

//移除链表第一个节点
public E pollFirst() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

//移除链表最后一个节点
public E pollLast() {
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}

//在链表头部添加数据
public void push(E e) {
  	addFirst(e);
}

//移除链表第一个数据
public E pop() {
  return removeFirst();
}

//移除从链表头部到尾部第一次出现数据o的节点
public boolean removeFirstOccurrence(Object o) {
  	return remove(o);
}

//移除从链表尾部到头部第一次出现数据o的节点
public boolean removeLastOccurrence(Object o) {
      if (o == null) {
        for (Node<E> x = last; x != null; x = x.prev) {
          if (x.item == null) {
            unlink(x);
            return true;
          }
        }
      } else {
        for (Node<E> x = last; x != null; x = x.prev) {
          if (o.equals(x.item)) {
            unlink(x);
            return true;
          }
        }
      }
      return false;
}

//获取next为index的迭代器
public ListIterator<E> listIterator(int index) {
    checkPositionIndex(index);
    return new ListItr(index);
}

//获取反向迭代器
public Iterator<E> descendingIterator() {
  	return new DescendingIterator();
}

//反向迭代器实现类
private class DescendingIterator implements Iterator<E> {
    private final ListItr itr = new ListItr(size());
  	// 反向迭代器是否下一个元素。
 	  // 实际上是判断双向链表的当前节点是否达到开头
    public boolean hasNext() {
      return itr.hasPrevious();
    }
  	// 反向迭代器获取下一个元素。
  	// 实际上是获取双向链表的前一个节点
    public E next() {
      return itr.previous();
    }
  	// 删除当前节点
    public void remove() {
      itr.remove();
    }
}

//调用父类克隆函数
private LinkedList<E> superClone() {
    try {
      return (LinkedList<E>) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new InternalError(e);
    }
}

// 克隆函数。返回LinkedList的克隆对象。
public Object clone() {
    LinkedList<E> clone = superClone();

    // 将克隆置于“原始”状态
    clone.first = clone.last = null;
    clone.size = 0;
    clone.modCount = 0;

    // 将链表中所有节点的数据都添加到克隆对象中
    for (Node<E> x = first; x != null; x = x.next)
      clone.add(x.item);

    return clone;
}

//返回LinkedList的Object[]数组
public Object[] toArray() {
  	// 新建Object[]数组
    Object[] result = new Object[size];
    int i = 0;
  	// 将链表中所有节点的数据都添加到Object[]数组中
    for (Node<E> x = first; x != null; x = x.next)
      result[i++] = x.item;
    return result;
}

// 返回LinkedList的模板数组。所谓模板数组,即可以将T设为任意的数据类型
public <T> T[] toArray(T[] a) {
  // 若数组a的大小 < LinkedList的元素个数(意味着数组a不能容纳LinkedList中全部元素)
  // 则新建一个T[]数组,T[]的大小为LinkedList大小,并将该T[]赋值给a。
    if (a.length < size)
      a = (T[])java.lang.reflect.Array.newInstance(
      a.getClass().getComponentType(), size);
    int i = 0;
  // 将链表中所有节点的数据都添加到数组a中
    Object[] result = a;
    for (Node<E> x = first; x != null; x = x.next)
      result[i++] = x.item;

    if (a.length > size)
      a[size] = null;

    return a;
}

// java.io.Serializable的写入函数
// 将LinkedList的“容量,所有的元素值”都写入到输出流中
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // Write out any hidden serialization magic
    s.defaultWriteObject();

    // 写入节点数
    s.writeInt(size);

    // 将链表中所有节点的数据都写入到输出流中
    for (Node<E> x = first; x != null; x = x.next)
      s.writeObject(x.item);
}

// java.io.Serializable的读取函数:根据写入方式反向读出
// 先将LinkedList的“容量”读出,然后将“所有的元素值”读出
private void readObject(java.io.ObjectInputStream s)
  throws java.io.IOException, ClassNotFoundException {
  // Read in any hidden serialization magic
  s.defaultReadObject();

  // 从输入流中读取“容量”
  int size = s.readInt();

  // 从输入流中将“所有的元素值”并逐个添加到链表中 	
  for (int i = 0; i < size; i++)
    linkLast((E)s.readObject());
}

引用其他博客的总结:skywang12345

  • LinkedList是通过双向链表来实现,其中链表节点为node,为LinkedList的内部类,属性包括该节点值、上一个节点,下一个节点。

  • 从LinkedList的实现方式中可以发现,它不存在LinkedList容量不足的问题。

  • LinkedList的克隆函数,即是将全部元素克隆到一个新的LinkedList对象中。

  • LinkedList实现java.io.Serializable。当写入到输出流时,先写入“容量”,再依次写入“每一个节点保护的值”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。

  • 从LinkedList的实现方式中可以发现,它不存在LinkedList容量不足的问题。

  • 由于LinkedList实现了Deque,而Deque接口定义了在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。

            第一个元素(头部)                 最后一个元素(尾部)
            抛出异常        特殊值            抛出异常        特殊值
    插入    addFirst(e)    offerFirst(e)    addLast(e)        offerLast(e)
    移除    removeFirst()  pollFirst()      removeLast()    pollLast()
    检查    getFirst()     peekFirst()      getLast()        peekLast()
    
  • LinkedList可以作为FIFO(先进先出)的队列,作为FIFO的队列时,下表的方法等价:

    队列方法       等效方法
    add(e)        addLast(e)
    offer(e)      offerLast(e)
    remove()      removeFirst()
    poll()        pollFirst()
    element()     getFirst()
    peek()        peekFirst()
    
  • LinkedList可以作为LIFO(后进先出)的栈,作为LIFO的栈时,下表的方法等价:

    栈方法        等效方法
    push(e)      addFirst(e)
    pop()        removeFirst()
    peek()       peekFirst()
    

以上是关于LinkedList源码分析的主要内容,如果未能解决你的问题,请参考以下文章

linkedList源码分析

LinkedList源代码深入剖析

JDK源码LinkedList源码分析

LinkedList源码分析

LinkedList源码分析

LinkedList源码分析--jdk1.8