LinkedList源码学习

2020-07-16 17:38:18 浏览数 (2)

LinkedList 继承 抽象SequentialList、实现list接口,双端队列Deque以及克隆,因此具备列表、队列、双端队列的特性,可克隆。

1.相关变量信息

代码语言:javascript复制
//长度为0
transient int size = 0;
//首节点
transient Node<E> first;
//尾节点
transient Node<E> last;

//节点信息包含当前节点元素,下一个元素节点、//前一个元素节点,也就是前驱和后继,//类似于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;
    }
}

2.构造函数

代码语言:javascript复制
//空参构造方法
public LinkedList() {
}
//构造一个包含指定集合元素的列表,//其顺序由集合的迭代器返回public LinkedList(Collection<? extends E> c) {
//指向空参构造
    this();
//添加集合c
    addAll(c);
}


其中addAll方法:
//将集合c的元素加入到列表中
public boolean addAll(Collection<? extends E> c) {
   return addAll(size, c);
}
//插入带有指定元素的集合c到list列表中public boolean addAll(int index,                      Collection<? extends E> c) {//检查当前位子的索引是否越界
checkPositionIndex(index);

//将带有特定元素的集合c转成数组
Object[] a = c.toArray();
//拿到数组的长度
int numNew = a.length;
//如果长度为0,返回false,不进行添加
 if (numNew == 0)
    return false;
//定义节点pred 前节点、succ后节点
 Node<E> pred, succ;
//如果索引等于列表长度,此时说明//最后一个元素,直接添加即可, if (index == size) {
   succ = null;
   pred = last;
//否者,进行前驱和后继操作
 } else {
  //找到当前下标指向的节点,要在该节点  //前插入数据,因此令succ等于该节点。  succ = node(index);
  //前节点等于succ的前驱节点
     pred = succ.prev;
  }

//对c.array中的元素进行遍历,并将//其元素都强转成E类型  for (Object o : a) {
    @SuppressWarnings("unchecked")     E e = (E) o;//创建一个新的节点对象,节点元素为e,//前节点为pred,后节点为null Node<E> newNode = new Node<>(pred, e, null);
//如果前节点为null,则将当前节点的//信息变为头结点信息 if (pred == null)
   first = newNode;
//否者,将当前节点变成前节点的后一个节点
else
   pred.next = newNode;//令当前节点作为前节点,获取下一个元素,循环   pred = newNode;  }

//说明是从当前集合的末尾开始插入数据,//因此数据的最后一个元素,作为当前集合的last if (succ == null) {
   last = pred;
//后节点不为null,pred为新插入的//最后一个数据,令其的后节点等于之前拆开//位置的后节点,succ为之前拆开位置//的前节点,令其前节点prev等于新插入//的元素的最后一个数据  } else {
      pred.next = succ;
      succ.prev = pred;
    }
//由于增加了元素,因此需要增加元素的长度
  size  = numNew;
  modCount  ;
  return true;}

3.相关操作

(1)添加元素

头插:

代码语言:javascript复制
//添加头结点
public void addFirst(E e) {
    linkFirst(e);
}

//增加元素e为头节点元素,头插
private void linkFirst(E e) {
//f为首节点final Node<E> f = first;//创建新节点,前驱节点为null,后继节点为first节点
final Node<E> newNode = new Node<>(null, e, f);
//更新first节点
first = newNode;
//如果f节点为空,则两个节点首节点等于尾节点
// 则表示newNode = new Node<>(null, e, null);
//说明此节点是last节点
if (f == null)
   last = newNode;
else
 //f不为空时,则newNode = new Node<>(null, e, f); //表示头节点不为空 //而当前节点在头节点前面,因此此时e变成f的前驱节点
  f.prev = newNode;
  //长度自增
  size  ;
 //修改次数自增
  modCount  ;
}

尾插:

代码语言:javascript复制
//添加尾元素
public void addLast(E e) {
    linkLast(e);
}

//增加e作为链表的尾节点元素,尾插
void linkLast(E e) {
 //将l设置为尾节点
 final Node<E> l = last;
 //创建新节点
 final Node<E> newNode = new Node<>(l, e, null);
 //将当前节点变成尾节点
 last = newNode;
 //如果l==null => //newNode = new Node<>(null, e, null) //因此说明此元素是头结点
 if (l == null)
   first = newNode;
 //l不为空,在l的后面,则增加元素往l的后面插
else
      l.next = newNode;
    size  ;
    modCount  ;
}

尾插:

代码语言:javascript复制
//添加元素,尾插
public boolean add(E e) {
    linkLast(e);
    return true;
}

//插入带有指定元素的集合c到list列表中
public boolean addAll(int index,                   Collection<? extends E> c) {//检查当前位子的索引是否越界
checkPositionIndex(index);

//将带有特定元素的集合c转成数组
Object[] a = c.toArray();
//拿到数组的长度
int numNew = a.length;
//如果长度为0,返回false,不进行添加
 if (numNew == 0)
    return false;
//定义节点pred 前节点、succ后节点
    Node<E> pred, succ;
//如果索引等于列表长度,//此时说明最后一个元素,直接添加即可,    if (index == size) {
       succ = null;
       pred = last;
   //否者,进行前驱和后继操作
   } else {
       //找到当前下标指向的节点,要在该节点       //前插入数据,因此令succ等于该节点。       succ = node(index);
       //前节点等于succ的前驱节点
       pred = succ.prev;
    }

    //对c.array中的元素进行遍历,    //并将其元素都强转成E类型    for (Object o : a) {
       @SuppressWarnings("unchecked")        E e = (E) o;    //创建一个新的节点对象,节点元素为e,    //前节点为pred,后节点为null    Node<E> newNode = new Node<>(pred, e, null);
        //如果前节点为null,则将当前节点的信息        //变为头结点信息        if (pred == null)
           first = newNode;
//否者,将当前节点变成前节点的后一个节点,//将当前节点作为前节点,获取下一个节点,循环else
          pred.next = newNode;        pred = newNode; 
    }

    //说明是从当前集合的末尾开始插入数据,    //因此数据的最后一个元素,作为当前集合的last    if (succ == null) {
        last = pred;
    //后节点不为null,pred为新插入的最后一个数据,    //令其的后节点等于之前拆开    // 位置的后节点,succ为之前拆开位置的前节点,    //令其前节点prev等于新插入的元素的最后一个数据    } else {
        pred.next = succ;
        succ.prev = pred;
    }
    //由于增加了元素,因此需要增加元素的长度
    size  = numNew;
    modCount  ;
    return true;
}

中间插:

代码语言:javascript复制
//添加指定元素
public void add(int index, E element) {
    checkPositionIndex(index);
    //所要添加元素等于长度,则尾插
    if (index == size)
        linkLast(element);
//否者之间插
else
        linkBefore(element, node(index));
}

//传入元素和后节点,中间插
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;
    //如果前节点为null,则将新节点变成首节点即可
    if (pred == null)
        first = newNode;
//如果前节点不为空,则为前节点的后一个元素
else
      pred.next = newNode;
    size  ;
    modCount  ;
}

添加指定元素:

代码语言:javascript复制
//将集合c的元素加入到列表中
public boolean addAll(Collection<? extends E> c){
    return addAll(size, c);
}
(2)删除操作,释放节点

删除头结点:

代码语言:javascript复制
//删除头结点
public E removeFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    //删除头结点信息
    return unlinkFirst(f);
}

//释放头结点
private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    //获取元素信息
    final E element = f.item;
    //拿到后继节点信息
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    //将下一个节点变成头结点
    first = next;
    if (next == null)
        last = null;
else
        //同时将头结点的前驱节点置空
        next.prev = null;
    //长度-1
    size--;
    //修改次数 1
    modCount  ;
    //返回释放的元素
    return element;
}

删除尾节点:

代码语言:javascript复制
//删除尾节点
public E removeLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    //删除尾节点
    return unlinkLast(l);
}

//释放尾节点
private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    //获取当前元素
    final E element = l.item;
    //获取尾节点的前驱节点
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // help GC
    //将为节点的前驱节点变成尾节点
    last = prev;
    if (prev == null)
        first = null;
else
        //同时将尾节点置空
        prev.next = null;
    size--;
    modCount  ;
    return element;
}

删除中间节点:

代码语言:javascript复制
//删除元素
public boolean remove(Object o) {
  //删除的对象不为null
  if (o == null) {
   //进行遍历
   for(Node<E> x = first; x != null; x=x.next){
            //不为null,进行移除
            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;
}

//释放节点
E unlink(Node<E> x) {
    // assert x != null;
    //获取要释放的元素
    final E element = x.item;
    //获取前驱节点和后继节点
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

   //如果前驱是空的,则说明释放元素就是   //头结点,将后面元素变成头节点    if (prev == null) {
        first = next; //一个元素的情况
   //如果前驱不为null,则说明当前节点有前驱节点,
   // 则只需将前驱节点的next=当前节点的next,
   // 同时还需要删除节点的引用,因为此时不可用
    } else {
        prev.next = next;
        x.prev = null;
    }
    //如果后继为空,则前置节点成为最后一个节点
    if (next == null) {
        last = prev;
    //后继节点不为空,则后继点击前移
    //将要删除的节点的后继引用删除
    } else {
        next.prev = prev;
        x.next = null;
    }
   //将要删除的元素置空,长度减1,   //修改次数 1,返回要删除的节点元素    x.item = null;
    size--;
    modCount  ;
    return element;
}
(3)获取get操作

获取头结点元素:

代码语言:javascript复制
//获取头结点
public E getFirst() {
    final Node<E> f = first;
    //进行判空
    if (f == null)
        throw new NoSuchElementException();
    //返回元素信息
    return f.item;
}

获取尾节点元素:

代码语言:javascript复制
//获取尾节点
public E getLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return l.item;
}

获取指定节点元素:

代码语言:javascript复制
//获取元素
public E get(int index) {
    //先检查是否越界
    checkElementIndex(index);
    //返回元素信息
    return node(index).item;
}

(4)替换操作:

代码语言:javascript复制
//替换指定元素
public E set(int index, E element) {
    //检查越界情况
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element; //替换节点元素既可
    return oldVal;
}

(5)返回元素的索引信息:

代码语言:javascript复制
//返回第一次出现的索引信息
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;
}

//返回最后出现的索引信息
public int lastIndexOf(Object o) {
  int index = size;
  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;
}

4.队列操作

代码语言:javascript复制
//返回首节点元素信息
public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

//获取头结点元素
public E element() {
    return getFirst();
}
//移除头结点
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

//删除头结点
public E remove() {
    return removeFirst();
}
//添加元素到队尾
public boolean offer(E e) {
    return add(e);
}

5.双端队列操作

代码语言:javascript复制
//头插,添加元素
public boolean offerFirst(E e) {
    addFirst(e);
    return true;
}

//尾插,添加元素
public boolean offerLast(E e) {
    addLast(e);
    return true;
}
//返回头节点的元素
public E peekFirst() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}
//返回尾节点的元素
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);
}

6.栈操作

代码语言:javascript复制
//由于支持栈操作,所以支持push头部添加元素,进栈
public void push(E e) {
    addFirst(e);
}
//支持栈操作,pop移除,也就是消费,出栈
public E pop() {
    return removeFirst();
}

7.迭代操作

代码语言:javascript复制
//进行元素迭代
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;
   }

    //是否进行下一次迭代
    public boolean hasNext() {
        return nextIndex < size;
    }

    //返回下一个元素
    public E next() {
        checkForComodification();
        if (!hasNext())
            throw new NoSuchElementException();
        //下一个元素
        lastReturned = next;
        //下一个元素作为参考,当前元素,next元素        //的节点就是next.next        next = next.next;
        //nextIndex自增
        nextIndex  ;
        return lastReturned.item;
    }

    //是否存在前驱元素
    public boolean hasPrevious() {
        return nextIndex > 0;
    }

   //存在前驱元素,则进行获取
   public E previous() {
     checkForComodification();
      if (!hasPrevious())
        throw new NoSuchElementException();

     lastReturned = next = (next == null) ? last                        : next.prev;     nextIndex--;
     return lastReturned.item;
    }

    public int nextIndex() {
        return nextIndex;
    }

    public int previousIndex() {
        return nextIndex - 1;
    }

    //进行remove操作
    public void remove() {
        checkForComodification();
        if (lastReturned == null)
            throw new IllegalStateException();

        Node<E> lastNext = lastReturned.next;
        unlink(lastReturned);
        if (next == lastReturned)
            next = lastNext;
else
            nextIndex--;
        lastReturned = null;
        expectedModCount  ;
    }
  
    //元素替换
    public void set(E e) {
        if (lastReturned == null)
            throw new IllegalStateException();
        checkForComodification();
        lastReturned.item = e;
    }

    //添加元素
    public void add(E e) {
        checkForComodification();
        lastReturned = null;
        if (next == null)
            linkLast(e);
else
            linkBefore(e, next);
        nextIndex  ;
        expectedModCount  ;
    }

//进行迭代
public void forEachRemaining(Consumer<?                            super E> action){    Objects.requireNonNull(action);
    while (modCount == expectedModCount &&                               nextIndex < size) {        action.accept(next.item);
        lastReturned = next;
        next = next.next;
        nextIndex  ;
       }
       checkForComodification();
    }
  
 //检查修改次数与期望修改此时是否相同
 final void checkForComodification() {
  if (modCount != expectedModCount)
   throw new ConcurrentModificationException();
    }
}

8.克隆操作

代码语言:javascript复制
public Object clone() {
 LinkedList<E> clone = superClone();

  // Put clone into "virgin" state
  clone.first = clone.last = null;
  clone.size = 0;
  clone.modCount = 0;

 // Initialize clone with our elements
 for (Node<E> x = first; x != null; x = x.next)
    clone.add(x.item);

    return clone;
}

9.序列化操作

代码语言:javascript复制
//将LinkedList写入流中,也就是将LinkedList保存到流中,//进行自己的个性化序列化//包括序列化数字、size、元素、节点信息
private void writeObject(java.io.                           ObjectOutputStream s)    throws java.io.IOException {
    // Write out any hidden serialization magic
    s.defaultWriteObject();

    // Write out size
    s.writeInt(size);

 // Write out all elements in the proper order.
 for (Node<E> x = first; x != null; x = x.next)
    s.writeObject(x.item);
}

//反序列化,从流中将LinkedList读取出来
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
   // Read in any hidden serialization magic
   s.defaultReadObject();

   // Read in size
   int size = s.readInt();

    // Read in all elements in the proper order.
    for (int i = 0; i < size; i  )
        linkLast((E)s.readObject());
}

当然JDK1.8时出现了迭代 public Spliterator<E> spliterator(),功能与迭代器类似。由于其具有队列和链表的特性,同时还可以像栈一样操作,因此我们可以知道其顺序访问的速度较快,随机访问的速度较慢,插入、删除速度快。

0 人点赞