LinkedList详解

原文链接http://zhhll.icu/2021/01/04/java%E5%9F%BA%E7%A1%80/%E9%9B%86%E5%90%88/LinkedList%E8%AF%A6%E8%A7%A3/

LinkedList详解

LinkedList是List接口的一个主要的实现类之一。以java8为例来了解一下LinkedList的源码实现

继承关系

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

继承了AbstractSequentialList,实现了List接口、Deque接口、Cloneable接口、Serializable接口

关键变量

/*** 链表的长度*/transient int size = 0;/** * 头节点 * Invariant: (first == null && last == null) || *            (first.prev == null && first.item != null) */transient Node<E> first;/** * 尾结点 * Invariant: (first == null && last == null) || *            (last.next == null && last.item != null) */transient Node<E> last;// 这里用到了一个内部类Node,看到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;  }}

构造器

// 无参默认构造器public LinkedList() {}/** * 传入一个集合作为参数 */public LinkedList(Collection<? extends E> c) {    this();    addAll(c);}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;// 前置节点和后置节点  Node<E> pred, succ;  // index == size时表示是从链表尾部添加元素,所以前置节点为当前链表的尾结点  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);    // 前置节点为null,表示这是头节点    if (pred == null)      first = newNode;    else      pred.next = newNode;    pred = newNode;  }// succ == null,说明是从尾部添加的,将最后一个节点赋给尾结点  if (succ == null) {    last = pred;  } else {    pred.next = succ;    succ.prev = pred;  }  size += numNew;  modCount++;  return true;}// 获取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;  }}

add方法

// 逻辑比较简单,与上边addAll类似,一通插public void add(int index, E element) {    checkPositionIndex(index);// index == size表示在链表尾部添加    if (index == size)        linkLast(element);    else // 否则在中间插入  先找到index所在位置的节点        linkBefore(element, node(index));}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++;}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++;}

remove方法

LinkedList的remove方法比较

public E remove(int index) {  // 检测该索引位置是否符合要求  index >= 0 && index < size    checkElementIndex(index);    return unlink(node(index));}// 找到当前索引位置的节点Node<E> node(int index) {  // assert isElementIndex(index);  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;  }}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;// 前驱节点为null,表示为头节点,头节点指向后继节点即可  if (prev == null) {    first = next;  } else {    // 前驱节点的后继节点指向该元素的后继节点    prev.next = next;    x.prev = null;  }// 后继节点为null,表示为尾节点,尾节点指向前驱节点即可  if (next == null) {    last = prev;  } else {    // 后继节点的前驱节点指向该元素的前驱节点    next.prev = prev;    x.next = null;  }  x.item = null;  size--;  modCount++;  return element;}

迭代器

LinkedList使用的迭代器是其父类AbstractSequentialList中的iterator方法

// AbstractSequentialList类方法public Iterator<E> iterator() {    return listIterator();}// LinkedList类方法public ListIterator<E> listIterator(int index) {  checkPositionIndex(index);  return new ListItr(index);}
//  与ArrayList的迭代器不同的是,ArrayList的迭代器实现的是Iterator接口 而LinkedList为ListIterator接口,相当于ArrayList的listIterator()方法//  Iterator只能向前遍历,ListIterator可以向前也可以向后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;        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;    }    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();     }}

由于本身的博客百度没有收录,博客地址http://zhhll.icu

(0)

相关推荐

  • 数据驱动型的设计02

    本系列从数据结构相关的计算机知识出发,从数据的角度提出一些数据驱动的设计思维模式. 第01期总体介绍数据结构与设计的关系,用数据结构的方式来思考设计,并通过几个案例介绍一些大的思路. 第02期介绍数据 ...

  • 面试官:兄弟,说说 ArrayList 和 LinkedList 有什么区别

    来自公众号:沉默王二 ArrayList 和 LinkedList 有什么区别,是面试官非常喜欢问的一个问题.可能大部分小伙伴和我一样,能回答出"ArrayList 是基于数组实现的,Lin ...

  • 胎元命宫详解

    胎元命宫详解 胎元命宫 8.1 胎元 胎, 指人受精怀胎的月份. 其起法是: 人生月后紧接着这个月的天干与生月后第三个月的地支相配, 就为胎元. 如1998年八月生人, 八月为辛酉, 辛后一干是壬, ...

  • 批八字算婚姻详解

    批八字算婚姻详解 很多人喜欢在孩子一出生的时候就给他们算一下八字,因为他们相信孩子的八字和命运是相对注定了的,通过算命之后可以顺利的避免一些可能在生活中遇到的一些问题和坎坷,也可以顺利度过一些&quo ...

  • 电视选购12个重要参数详解,看完你就是专家,附:爆款推荐

    本内容来源于@什么值得买APP,观点仅代表作者本人 |作者:白云上的鱼 创作立场声明:分享电视选购知识,重要参数详解,轻松搞定电视选购. 目前电视的选择太多太多了,品牌百花齐放琳琅满目,各种高科技加成 ...

  • 倪海厦:病是问出来的|问诊十法详解

    倪海厦,美国经方中医,被喻为当代少见的"命.相.卜.山.医"五术兼备之旷世奇人. (倪师)中医的问诊十个法则 我们经方家的问诊非常重要,因此有必要为读者说明一下,如何找经方家看病, ...

  • 为何医生让他把氨氯地平换成缬沙坦?药师详解两类降压药的好与坏

    硝苯地平.氨氯地平.缬沙坦.氯沙坦等等,这些降压药都是高血压患者常用的降压药.从名字中也可以看出这些降压药属于两类不同的降压药,一种是地平类,即为钙离子拮抗剂(CCB),另外一种是沙坦类,即为血管紧张 ...

  • 几何探究类压轴题:精编20例及详解

    成才路上 初中精品学习资料 104篇原创内容 公众号 / END /

  • 高考物理11类重点题型全解析! 附经典例题&详解

    高考理科综合卷中,物理部分选择题有单项和双项选择题两种题型.从最近几年的试题看: 4道单项选择难度低,考查的考点相对稳定且相对单一,涉及的知识点主要有共点力平衡.热力学第一定律.气体状态方程.分子动理 ...

  • 【同步讲练】七年级下册:二元一次方程组七种典型例题详解,一次解决应用问题!

    【同步讲练】七年级下册:二元一次方程组七种典型例题详解,一次解决应用问题!

  • 行书基础笔法详解,以兰亭序为例,建议收藏学习

    学好行书 4篇原创内容 公众号 欢迎您查看行书名帖 在行书的书写中,我们一方面要注意其字形,另一方面更要注意笔画的写法,因为笔画是字的基本构成元素.因此,把握好每个笔画的写法是最为重要的一个学习环节. ...