03. 数据结构之链表

news2024/11/24 2:20:43

前言

链表是相区别于数组的,另一种典型的线性表数据结构。也是学习后面复杂的数据结构的基础,我们后面很多结构比如树,有向图等都可以用链表很方便的存储管理。

1. 概念

链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。

链表中数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

常见的链表包括:单链表、双向链表、循环链表

1.1 单链表

1.1.1 什么是单链表

单向链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。

1.1.2 节点结构

Node{
	int data;
	Node next;
}

1.1.3 图示

在这里插入图片描述

1.2 双向链表

1.2.1 什么是双向链表

双向链表的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev指针。

1.2.2 节点结构

Node{
	int data;
	Node next;
	Node prev;
}

1.2.3 图示

在这里插入图片描述

1.3 循环链表

1.3.1 什么是循环链表

链表的尾节点指向头节点形成一个环,称为循环链表

1.3.2 节点结构

Node{
	int data;
	Node next;
}

1.3.2 图示

在这里插入图片描述

2. 存储原理

链表在内存中的存储方式则是随机存储(链式存储)。
链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。

链表的第1个节点被称为头节点,没有任何节点的next指针指向它,或者说它的前置节点为空。头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。链表的最后1个节点被称为尾节点,它指向的next为空。

3.操作

后面演示统一以单链表为例,其他类型链表操作类似。

3.1 定义节点类

package org.wanlong.linkedList;

/**
 * @author wanlong
 * @version 1.0
 * @description: 节点定义
 * @date 2023/5/22 15:15
 */
public class Node {

    int id;
    String name;

    //下一个节点
    Node next;

    public Node(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "Node{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

3.2 插入节点

3.2.1 头插法

package org.wanlong.linkedList;

/**
 * @author wanlong
 * @version 1.0
 * @description: 单链表测试验证
 * @date 2023/5/22 15:16
 */
public class SingleLinkedList {


    //初始化头节点
    private Node head = new Node(0, "");

    /**
     * @Description: 头部添加节点
     * @Author: wanlong
     * @Date: 2023/5/22 15:17
     * @param node:
     * @return void
     **/
    public void addNodeOnHead(Node node) {
        //temp 也指向头结点
        Node temp=head;
        //新增节点指向临时节点,即查到头
        node.next=temp.next;
        head.next=node;

    }
}

3.2.2 尾插法

/**
 * @Description:插入链表末尾
 * @Author: wanlong
 * @Date: 2023/5/22 15:25
 * @param node:
 * @return void
 **/
public void addNodeOnTail(Node node) {
    //temp 也指向头结点
    Node temp=head;
    //找到尾节点
    while (true){
        if (temp.next==null){
            break;
        }
        //指针后移
        temp=temp.next;
    }
    //跳出循环后temp指向的就是尾节点,此时将新增节点插到尾节点后面
    temp.next=node;
}

3.2.3 中间插入

/**
 * @Description:将待插入节点node,插入beforeNode节点之后
 * @Author: wanlong
 * @Date: 2023/5/22 15:24
 * @param beforeNode: 基准节点
 * @param node:  待插入节点
 * @return void
 **/
public void addNodeOnMiddle(Node beforeNode,Node node) {
    //temp 也指向头结点
    Node temp=head;
    //找到尾节点
    while (temp!=null){
        if (temp.id== beforeNode.id){
            //如果相等,即为找到基准节点了,插入
            //1.node节点的指针指向基准节点的下一个节点
            node.next=temp.next;
            //2.基准节点指向node节点
            temp.next=node;
            break;
        }
        //指针后移
        temp=temp.next;
    }
    //如果到末尾节点还没找到,无法插入
    if (temp==null){
        System.err.println("没有找到基准节点");
    }
}

3.2 遍历查找

//遍历链表
public void showList() {
    //空链表
    if (head.next == null) {
        System.out.println("链表为空");
        return;
    }
    Node temp = head.next;
    while (true) {
        if (temp == null) {
            return;
        }
        System.out.println(temp);
        //指针下移
        temp = temp.next;
    }
}

3.3 更新

/**
 * @Description: 更新节点值,这里要留意,需要保存待更新节点的上一个节点,不然单链表指针会丢失
 * @Author: wanlong
 * @Date: 2023/5/22 15:57
 * @param orgNode: 原来节点
 * @param node:  要更新的节点
 * @return void
 **/
public void updateNode(Node orgNode,Node node) {
    //temp 也指向头结点
    Node temp=head;
    //找到尾节点
    while (temp.next!=null){
        if (temp.next.id== orgNode.id){
            //如果相等,即为找到基准节点了,插入
            //1.node节点的指针指向基准节点的下一个节点
            node.next=temp.next.next;
            //2.基准节点指向node节点
            temp.next=node;
            break;
        }
        //指针后移
        temp=temp.next;
    }
    //如果到末尾节点还没找到,无法插入
    if (temp.next==null){
        System.err.println("没有找到替换节点");
    }
}

3.4 删除

3.4.1 头部删除

 /**
 * @Description: 删除头结点
 * @Author: wanlong
 * @Date: 2023/5/22 16:09
 * @return void
 **/
public void deleteOnHead(){
    Node temp=head;
    //指针指向下下个节点
    temp.next=temp.next.next;
}

3.4.2 尾部删除

/**
 * @Description: 删除尾节点,这里要留意,保存尾节点的上一个节点,不然回丢失指针
 * @Author: wanlong
 * @Date: 2023/5/22 16:09
 * @return void
 **/
public void deleteOnTail(){
    //1. 找到尾节点
    Node temp=head;
    while (temp.next!=null && temp.next.next!=null){
        temp=temp.next;
    }
    //2、指向尾节点的指针置为空,尾节点没有指针指向,会被jvm回收
    temp.next=null;
}

3.4.3 中间删除

/**
 * @Description: 删除指定节点
 * @Author: wanlong
 * @Date: 2023/5/22 16:10
 * @param node:  待删除节点
 * @return void
 **/
public void deleteNode(Node node){
    Node temp=head;
    while (temp.next!=null ){
        if (temp.next.id== node.id){
            temp.next=temp.next.next;
            break;
        }
        temp=temp.next;
    }
    if (temp.next==null){
        System.out.println("没有找到待删除节点");
    }
}

3.5 测试验证

package org.wanlong.linkedList;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * @author wanlong
 * @version 1.0
 * @description:
 * @date 2023/5/22 15:32
 */
public class ClientTest {

    static SingleLinkedList singleLinkedList = new SingleLinkedList();

    @BeforeClass
    public static void init() {
        Node n1 = new Node(1, "张飞");
        Node n2 = new Node(2, "关羽");
        Node n3 = new Node(3, "赵云");
        Node n4 = new Node(4, "黄忠");
        Node n5 = new Node(5, "马超");

		//头插法初始化数据
        singleLinkedList.addNodeOnHead(n1);
        singleLinkedList.addNodeOnHead(n2);
        singleLinkedList.addNodeOnHead(n3);
        singleLinkedList.addNodeOnHead(n4);
        singleLinkedList.addNodeOnHead(n5);

        //Node{id=5, name='马超'}
        //Node{id=4, name='黄忠'}
        //Node{id=3, name='赵云'}
        //Node{id=2, name='关羽'}
        //Node{id=1, name='张飞'}

    }

    @Test
    public void testaddOnTail() {
        Node n6 = new Node(6, "刘备");

        singleLinkedList.addNodeOnTail(n6);

        //Node{id=5, name='马超'}
        //Node{id=4, name='黄忠'}
        //Node{id=3, name='赵云'}
        //Node{id=2, name='关羽'}
        //Node{id=1, name='张飞'}
        //Node{id=6, name='刘备'}
    }

    @Test
    public void testaddOnMiddle() {

        Node n5 = new Node(5, "马超");
        Node n7 = new Node(7, "诸葛亮");

        singleLinkedList.addNodeOnMiddle(n5, n7);
        //Node{id=5, name='马超'}
        //Node{id=7, name='诸葛亮'}
        //Node{id=4, name='黄忠'}
        //Node{id=3, name='赵云'}
        //Node{id=2, name='关羽'}
        //Node{id=1, name='张飞'}
    }

    @Test
    public void testUpdateNode() {

        Node orgNode = new Node(5, "马超");
        Node newNode = new Node(7, "诸葛亮");

        singleLinkedList.updateNode(orgNode, newNode);
        //Node{id=7, name='诸葛亮'}
        //Node{id=4, name='黄忠'}
        //Node{id=3, name='赵云'}
        //Node{id=2, name='关羽'}
        //Node{id=1, name='张飞'}
    }

    @Test
    public void testDeleteHead() {
        singleLinkedList.deleteOnHead();
        //Node{id=4, name='黄忠'}
        //Node{id=3, name='赵云'}
        //Node{id=2, name='关羽'}
        //Node{id=1, name='张飞'}
    }

    @Test
    public void testDeleteTail() {
        singleLinkedList.deleteOnTail();
//        Node{id=5, name='马超'}
//        Node{id=4, name='黄忠'}
//        Node{id=3, name='赵云'}
//        Node{id=2, name='关羽'}
    }

    @Test
    public void testDeleteNode() {
        Node orgNode = new Node(3, "赵云");
        singleLinkedList.deleteNode(orgNode);
        //Node{id=5, name='马超'}
        //Node{id=4, name='黄忠'}
        //Node{id=2, name='关羽'}
        //Node{id=1, name='张飞'}

        Node testNode = new Node(898, "猪八戒");
        singleLinkedList.deleteNode(testNode);
        //没有找到待删除节点

    }

    @AfterClass
    public static void showNode() {
        singleLinkedList.showList();
    }
}

4. 时间复杂度

  1. 查找节点因为只有指针,需要遍历链表,所以时间复杂度是:O(n)
  2. 插入,更新,删除因为只是涉及指针的移动,不涉及遍历,所以时间复杂度是:O(1)

5. 优缺点

5.1 优点

  1. 插入、删除、更新效率高,只涉及指针移动
  2. 省空间。严格来说链表因为要存储指针,实际存储的要比数组的内容多。不会节省空间。但是因为链表可以利用内存中的空间碎片,不需要像数组一样,分配连续的大片内存空间,所以链表指针占用的内存空间反倒可以忽略。尤其是针对特别大的对象的时候,此时甚至内存中就是没有足够分配的整个连续内存

5.2 缺点

  1. 查询效率较低,不能随机访问

6.应用

  1. 链表的应用也非常广泛,比如树、图、Redis的列表、LRU算法实现、消息队列
  2. 数组的优势在于能够快速定位元素,对于读操作多、写操作少的场景来说,用数组更合适一些
  3. 链表的优势在于能够灵活地进行插入和删除操作,如果需要在尾部频繁插入、删除元素,用链表更合适一些
  4. 数组和链表是线性数据存储的物理存储结构:即顺序存储和链式存储。

7.LinkedList源码解析

7.1 源码注释翻译

package java.util;

import java.util.function.Consumer;

/**
 * Doubly-linked list implementation of the {@code List} and {@code Deque}
 * interfaces.  Implements all optional list operations, and permits all
 * elements (including {@code null}).
 *
 *
 * <p>All of the operations perform as could be expected for a doubly-linked
 * list.  Operations that index into the list will traverse the list from
 * the beginning or the end, whichever is closer to the specified index.
 * <p>
 * list接口和Deque接口的双向链表实现。实现了list的所有操作。可以存储包含null的所有元素
 * 所有方法可以理解为是对一个双向链表的操作。索引到列表中的操作将从开始或结束遍历列表,以更接近指定索引的为准。
 *
 * <p><strong>Note that this implementation is not synchronized.</strong>
 * If multiple threads access a linked list concurrently, and at least
 * one of the threads modifies the list structurally, it <i>must</i> be
 * synchronized externally.  (A structural modification is any operation
 * that adds or deletes one or more elements; merely setting the value of
 * an element is not a structural modification.)  This is typically
 * accomplished by synchronizing on some object that naturally
 * encapsulates the list.
 * <p>
 * linkedlist 不是线程安全的。如果多个线程修改linkedlist对象,需要调用端外部加同步锁控制。
 * 这通常是通过对一些自然的对象进行同步来实现的封装列表
 * <p>
 * <p>
 * If no such object exists, the list should be "wrapped" using the
 * {@link Collections#synchronizedList Collections.synchronizedList}
 * method.  This is best done at creation time, to prevent accidental
 * unsynchronized access to the list:<pre>
 *   List list = Collections.synchronizedList(new LinkedList(...));</pre>
 * <p>
 * 如果没有这样的对象存在,应该用Collections.synchronizedList包装一下。最好是在创建的时候这样做
 * 来避免意外的非同步访问 比如: List list = Collections.synchronizedList(new LinkedList(...));
 *
 *
 * <p>The iterators returned by this class's {@code iterator} and
 * {@code listIterator} methods are <i>fail-fast</i>: if the list is
 * structurally modified at any time after the iterator is created, in
 * any way except through the Iterator's own {@code remove} or
 * {@code add} methods, the iterator will throw a {@link
 * ConcurrentModificationException}.  Thus, in the face of concurrent
 * modification, the iterator fails quickly and cleanly, rather than
 * risking arbitrary, non-deterministic behavior at an undetermined
 * time in the future.
 * <p>
 * 通过方法listIterator,iterator 返回的迭代器是快速失败的。如果在迭代器创建后,
 * 除了使用迭代器自己的 Iterator#remove() ,Iterator#add(Object) 修改list对象结构
 * 其他任何方式,任何时候,修改了这个list对象结构
 * 迭代器会抛出ConcurrentModificationException 异常
 * 因此,面对并发修改,迭代器快速而干净的失败。而不是在将来不确定的时间节点,出现可能的风险,不确定性行为
 *
 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
 * as it is, generally speaking, impossible to make any hard guarantees in the
 * presence of unsynchronized concurrent modification.  Fail-fast iterators
 * throw {@code ConcurrentModificationException} on a best-effort basis.
 * Therefore, it would be wrong to write a program that depended on this
 * exception for its correctness:   <i>the fail-fast behavior of iterators
 * should be used only to detect bugs.</i>
 * <p>
 * <p>
 * 注意,迭代器的快速失败行为不能确保一定生效。通常来说,在一个不加同步锁的并发修改中,做出任何坚决保证是不可能的。
 * Fail-fast iterators 尽最大努力会抛出ConcurrentModificationException异常
 * 因此,写一个 依赖这个异常 确保程序正确性 是不正确的做法,迭代器的快速失败行为应该只是用来检测bug
 *
 * <p>This class is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @param <E> the type of elements held in this collection
 * @author Josh Bloch
 * @see List
 * @see ArrayList
 * @since 1.2
 */

public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
    transient int size = 0;

    /**
     * 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;

    /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     * <p>
     * 创建一个linkedlist,包含指定集合的所有元素,顺序和指定集合迭代器指定的顺序一致
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

    /**
     * Links e as first element.
     * 第一个节点前面插入元素
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

    /**
     * Links e as last element.
     * 最后一个节点后面插入元素
     */
    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++;
    }

    /**
     * Inserts element e before non-null Node succ.
     * 插入一个元素在非空元素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++;
    }

    /**
     * Unlinks non-null first node 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;
        size--;
        modCount++;
        return element;
    }

    /**
     * Unlinks non-null last node 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;
    }

    /**
     * Unlinks non-null node x.
     * 删除非空元素x,注意这里时间复杂度是O(1),调用的时候,已经确定是哪个节点,有指针指向了node  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;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

    /**
     * Returns the first element in this list.
     * <p>
     * 获取list中的第一个元素
     *
     * @return the first element in this list
     * @throws NoSuchElementException if this list is empty
     */
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

    /**
     * Returns the last element in this list.
     * 获取最后一个元素
     *
     * @return the last element in this list
     * @throws NoSuchElementException if this list is empty
     */
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

    /**
     * Removes and returns the first element from this list.
     * 删除并返回第一个元素
     *
     * @return the first element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    /**
     * Removes and returns the last element from this list.
     * 删除并返回最后一个元素
     *
     * @return the last element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

    /**
     * Inserts the specified element at the beginning of this list.
     * 在开始插入指定元素
     *
     * @param e the element to add
     */
    public void addFirst(E e) {
        linkFirst(e);
    }

    /**
     * Appends the specified element to the end of this list.
     * 在末尾插入指定元素
     * <p>This method is equivalent to {@link #add}.
     *
     * @param e the element to add
     */
    public void addLast(E e) {
        linkLast(e);
    }

    /**
     * Returns {@code true} if this list contains the specified element.
     * More formally, returns {@code true} if and only if this list contains
     * at least one element {@code e} such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
     * <p>
     * 如果包含,返回TRUE
     *
     * @param o element whose presence in this list is to be tested
     * @return {@code true} if this list contains the specified element
     */
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

    /**
     * Returns the number of elements in this list.
     * 返回list包含元素数量
     *
     * @return the number of elements in this list
     */
    public int size() {
        return size;
    }

    /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     * <p>
     * 添加指定元素在末尾 和 addlast方法等价
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If this list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * {@code i} such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns {@code true} if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     * <p>
     * 删除list中第一个出现的指定元素,如果存在的话
     * 如果list不包含指定元素,list不变
     *
     * @param o element to be removed from this list, if present
     * @return {@code true} if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            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;
    }

    /**
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the specified
     * collection's iterator.  The behavior of this operation is undefined if
     * the specified collection is modified while the operation is in
     * progress.  (Note that this will occur if the specified collection is
     * this list, and it's nonempty.)
     * <p>
     * 添加指定集合所有元素到list末尾。顺序和指定集合迭代器顺序一致
     * 如果添加的时候,指定集合修改了,发生的事情是不可预知的,所以禁止这样做
     *
     * @param c collection containing elements to be added to this list
     * @return {@code true} if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

    /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     * <p>
     * 在指定的位置开始,插入指定的集合到list中。指定的坐标左边不变,右边元素右移 指定集合个单位
     *
     * @param index index at which to insert the first element
     *              from the specified collection
     * @param c     collection containing elements to be added to this list
     * @return {@code true} if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException      if the specified collection is null
     */
    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) {
            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;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

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

    /**
     * Removes all of the elements from this list.
     * The list will be empty after this call returns.
     * <p>
     * 删除list 所有元素
     */
    public void clear() {
        // Clearing all of the links between nodes is "unnecessary", but:
        // - helps a generational GC if the discarded nodes inhabit
        //   more than one generation
        // - is sure to free memory even if there is a reachable Iterator
        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++;
    }


    // Positional Access Operations

    /**
     * Returns the element at the specified position in this list.
     * 返回list指定位置的元素
     *
     * @param index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    /**
     * Replaces the element at the specified position in this list with the
     * specified element.
     * <p>
     * 替换指定下标的元素
     *
     * @param index   index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

    /**
     * Inserts the specified element at the specified position in this list.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (adds one to their indices).
     * <p>
     * 添加指定位置的元素
     *
     * @param index   index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    /**
     * Removes the element at the specified position in this list.  Shifts any
     * subsequent elements to the left (subtracts one from their indices).
     * Returns the element that was removed from the list.
     * <p>
     * 删除指定位置的元素
     *
     * @param index the index of the element to be removed
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

    /**
     * Tells if the argument is the index of an existing element.
     * 判断这个index 是否是存在的元素的位置
     */
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

    /**
     * Tells if the argument is the index of a valid position for an
     * iterator or an add operation.
     * <p>
     * 判断这个参数对一个迭代器或者一个操作来说是不是一个有效的位置
     */
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

    /**
     * Constructs an IndexOutOfBoundsException detail message.
     * Of the many possible refactorings of the error handling code,
     * this "outlining" performs best with both server and client VMs.
     */
    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));
    }

    /**
     * Returns the (non-null) Node at the specified element 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;
        }
    }

    // Search Operations

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index {@code i} such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     * <p>
     * 返回指定元素在集合中的第一次出现的下标,如果不存在返回-1
     *
     * @param o element to search for
     * @return the index of the first occurrence of the specified element in
     * this list, or -1 if this list does not contain the element
     */
    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;
    }

    /**
     * Returns the index of the last occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the highest index {@code i} such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     * <p>
     * 返回指定元素在集合中的最后一次出现的下标,如果不存在返回-1
     *
     * @param o element to search for
     * @return the index of the last occurrence of the specified element in
     * this list, or -1 if this list does not contain the element
     */
    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;
    }

    // Queue operations.

    /**
     * Retrieves, but does not remove, the head (first element) of this list.
     * <p>
     * 取出list的第一个元素,但是不删除
     *
     * @return the head of this list, or {@code null} if this list is empty
     * @since 1.5
     */
    public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

    /**
     * Retrieves, but does not remove, the head (first element) of this list.
     * <p>
     * 获取第一个元素
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */
    public E element() {
        return getFirst();
    }

    /**
     * Retrieves and removes the head (first element) of this list.
     * 取出并删除第一个元素
     *
     * @return the head of this list, or {@code null} if this list is empty
     * @since 1.5
     */
    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    /**
     * Retrieves and removes the head (first element) of this list.
     * 删除并返回第一个元素
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */
    public E remove() {
        return removeFirst();
    }

    /**
     * Adds the specified element as the tail (last element) of this list.
     * 添加指定元素在末尾
     *
     * @param e the element to add
     * @return {@code true} (as specified by {@link Queue#offer})
     * @since 1.5
     */
    public boolean offer(E e) {
        return add(e);
    }

    // Deque operations

    /**
     * Inserts the specified element at the front of this list.
     * 添加指定元素在首位
     *
     * @param e the element to insert
     * @return {@code true} (as specified by {@link Deque#offerFirst})
     * @since 1.6
     */
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    /**
     * Inserts the specified element at the end of this list.
     * 添加指定元素在末尾
     *
     * @param e the element to insert
     * @return {@code true} (as specified by {@link Deque#offerLast})
     * @since 1.6
     */
    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }

    /**
     * Retrieves, but does not remove, the first element of this list,
     * or returns {@code null} if this list is empty.
     * 取出但是不删除第一个元素,如果集合为空返回null
     *
     * @return the first element of this list, or {@code null}
     * if this list is empty
     * @since 1.6
     */
    public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

    /**
     * Retrieves, but does not remove, the last element of this list,
     * or returns {@code null} if this list is empty.
     *
     * @return the last element of this list, or {@code null}
     * if this list is empty
     * @since 1.6
     */
    public E peekLast() {
        final Node<E> l = last;
        return (l == null) ? null : l.item;
    }

    /**
     * Retrieves and removes the first element of this list,
     * or returns {@code null} if this list is empty.
     * <p>
     * 取出并删除第一个元素,如果集合为空返回null
     *
     * @return the first element of this list, or {@code null} if
     * this list is empty
     * @since 1.6
     */
    public E pollFirst() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    /**
     * Retrieves and removes the last element of this list,
     * or returns {@code null} if this list is empty.
     * 取出并删除最后一个元素,如果集合为空返回null
     *
     * @return the last element of this list, or {@code null} if
     * this list is empty
     * @since 1.6
     */
    public E pollLast() {
        final Node<E> l = last;
        return (l == null) ? null : unlinkLast(l);
    }

    /**
     * Pushes an element onto the stack represented by this list.  In other
     * words, inserts the element at the front of this list.
     * <p>
     * 推一个元素到这个list代表的栈中。换句话说,list前面插入一个元素
     * <p>This method is equivalent to {@link #addFirst}.
     *
     * @param e the element to push
     * @since 1.6
     */
    public void push(E e) {
        addFirst(e);
    }

    /**
     * Pops an element from the stack represented by this list.  In other
     * words, removes and returns the first element of this list.
     * <p>
     * 吐出一个元素从这个list代表的栈中。换句话说,删除这个list第一个元素
     *
     * <p>This method is equivalent to {@link #removeFirst()}.
     *
     * @return the element at the front of this list (which is the top
     * of the stack represented by this list)
     * @throws NoSuchElementException if this list is empty
     * @since 1.6
     */
    public E pop() {
        return removeFirst();
    }

    /**
     * Removes the first occurrence of the specified element in this
     * list (when traversing the list from head to tail).  If the list
     * does not contain the element, it is unchanged.
     * <p>
     * 删除list中第一次出现这个指定元素的节点
     *
     * @param o element to be removed from this list, if present
     * @return {@code true} if the list contained the specified element
     * @since 1.6
     */
    public boolean removeFirstOccurrence(Object o) {
        return remove(o);
    }

    /**
     * Removes the last occurrence of the specified element in this
     * list (when traversing the list from head to tail).  If the list
     * does not contain the element, it is unchanged.
     * <p>
     * 删除list中最后一次出现这个指定元素的节点
     *
     * @param o element to be removed from this list, if present
     * @return {@code true} if the list contained the specified element
     * @since 1.6
     */
    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;
    }

    /**
     * Returns a list-iterator of the elements in this list (in proper
     * sequence), starting at the specified position in the list.
     * Obeys the general contract of {@code List.listIterator(int)}.<p>
     * <p>
     * The list-iterator is <i>fail-fast</i>: if the list is structurally
     * modified at any time after the Iterator is created, in any way except
     * through the list-iterator's own {@code remove} or {@code add}
     * methods, the list-iterator will throw a
     * {@code ConcurrentModificationException}.  Thus, in the face of
     * concurrent modification, the iterator fails quickly and cleanly, rather
     * than risking arbitrary, non-deterministic behavior at an undetermined
     * time in the future.
     * <p>
     * 以正确的顺序,返回list迭代器处理list中的元素,在list中的指定的位置开始
     * 遵循List.listIterator的通用规则
     * list-iterator是快速失败的:如果在迭代器被创建后有任何结构修改,会抛出异常ConcurrentModificationException
     * 因此,面对并发修改,迭代器快速而干净的失败。而不是在将来不确定的时间节点,出现可能的风险,不确定性行为
     *
     * @param index index of the first element to be returned from the
     *              list-iterator (by a call to {@code next})
     * @return a ListIterator of the elements in this list (in proper
     * sequence), starting at the specified position in the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see List#listIterator(int)
     */
    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

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

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

    /**
     * @since 1.6
     */
    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }

    /**
     * Adapter to provide descending iterators via ListItr.previous
     * <p>
     * 通过ListItr.previor提供递减迭代器的适配器
     */
    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();
        }
    }

    @SuppressWarnings("unchecked")
    private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

    /**
     * Returns a shallow copy of this {@code LinkedList}. (The elements
     * themselves are not cloned.)
     * <p>
     * 返回一个浅拷贝对象
     *
     * @return a shallow copy of this {@code LinkedList} instance
     */
    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;
    }

    /**
     * Returns an array containing all of the elements in this list
     * in proper sequence (from first to last element).
     *
     * <p>The returned array will be "safe" in that no references to it are
     * maintained by this list.  (In other words, this method must allocate
     * a new array).  The caller is thus free to modify the returned array.
     *
     * <p>This method acts as bridge between array-based and collection-based
     * APIs.
     * <p>
     * 返回一个数组包含list集合所有元素,顺序和原来保持一致
     * 返回的数组是"安全"的,是因为list集合没有持有这个数组的引用
     * 换句话说,这个方法必须分配创建新的数组。调用者可以自由修改返回的数组,
     * 不用担心修改了会影响原来集合元素
     * <p>
     * 这个方法是数组操作和集合api的桥梁,即需要数组操作的时候,通过这个方法转换,即可实现集合转数组
     *
     * @return an array containing all of the elements in this list
     * in proper sequence
     */
    public Object[] toArray() {
        Object[] result = new Object[size];
        int i = 0;
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
        return result;
    }

    /**
     * Returns an array containing all of the elements in this list in
     * proper sequence (from first to last element); the runtime type of
     * the returned array is that of the specified array.  If the list fits
     * in the specified array, it is returned therein.  Otherwise, a new
     * array is allocated with the runtime type of the specified array and
     * the size of this list.
     * <p>
     * 返回一个数组包含list集合所有元素,顺序和原来保持一致
     * 返回数组的类型是指定的,如果这个list就是这个类型,那么直接返回,否则
     * 会根据指定的类型,和list大小创建一个新的数组
     *
     * <p>If the list fits in the specified array with room to spare (i.e.,
     * the array has more elements than the list), the element in the array
     * immediately following the end of the list is set to {@code null}.
     * (This is useful in determining the length of the list <i>only</i> if
     * the caller knows that the list does not contain any null elements.)
     * <p>
     * 如果数组大小比list大,那么数组最后的元素会被填充为null
     * 如果调用者指定list不包含空元素,这有助于确定list长度
     *
     *
     * <p>Like the {@link #toArray()} method, this method acts as bridge between
     * array-based and collection-based APIs.  Further, this method allows
     * precise control over the runtime type of the output array, and may,
     * under certain circumstances, be used to save allocation costs.
     * <p>
     * 类似toArray方法,这个方法也是集合和数组的桥梁。更近一步的是,这个方法允许对数组输出类型的更精确的控制。
     * 所以,在确定的条件下,可以节省分配成本
     *
     * <p>Suppose {@code x} is a list known to contain only strings.
     * The following code can be used to dump the list into a newly
     * allocated array of {@code String}:
     * <p>
     * 猜测x 是一个已知的确定只包含字符串的list ,下面的代码可以被用来将这个列表转储到一个新的分配的字符串数组中
     *
     * <pre>
     *     String[] y = x.toArray(new String[0]);</pre>
     * <p>
     * Note that {@code toArray(new Object[0])} is identical in function to
     * {@code toArray()}.
     * <p>
     * 注意,toArray(new Object[0]) 和 函数toArray() 是相同的
     *
     * @param a the array into which the elements of the list are to
     *          be stored, if it is big enough; otherwise, a new array of the
     *          same runtime type is allocated for this purpose.
     * @return an array containing the elements of the list
     * @throws ArrayStoreException  if the runtime type of the specified array
     *                              is not a supertype of the runtime type of every element in
     *                              this list
     * @throws NullPointerException if the specified array is null
     */
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            a = (T[]) java.lang.reflect.Array.newInstance(
                    a.getClass().getComponentType(), size);
        int i = 0;
        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;
    }

    private static final long serialVersionUID = 876323262645176354L;

    /**
     * Saves the state of this {@code LinkedList} instance to a stream
     * (that is, serializes it).
     * <p>
     * 序列化
     *
     * @serialData The size of the list (the number of elements it
     * contains) is emitted (int), followed by all of its
     * elements (each an Object) in the proper order.
     */
    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);
    }

    /**
     * Reconstitutes this {@code LinkedList} instance from a stream
     * (that is, deserializes it).
     * <p>
     * 反序列化
     */
    @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());
    }

    /**
     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
     * list.
     *
     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
     * {@link Spliterator#ORDERED}.  Overriding implementations should document
     * the reporting of additional characteristic values.
     * <p>
     * 创建一个并行迭代器
     *
     * @return a {@code Spliterator} over the elements in this list
     * @implNote The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED}
     * and implements {@code trySplit} to permit limited parallelism..
     * @since 1.8
     */
    @Override
    public Spliterator<E> spliterator() {
        return new LLSpliterator<E>(this, -1, 0);
    }

    /**
     * A customized variant of Spliterators.IteratorSpliterator
     */
    static final class LLSpliterator<E> implements Spliterator<E> {
        static final int BATCH_UNIT = 1 << 10;  // batch array size increment
        static final int MAX_BATCH = 1 << 25;  // max batch array size;
        final LinkedList<E> list; // null OK unless traversed
        Node<E> current;      // current node; null until initialized
        int est;              // size estimate; -1 until first needed
        int expectedModCount; // initialized when est set
        int batch;            // batch size for splits

        LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
            this.list = list;
            this.est = est;
            this.expectedModCount = expectedModCount;
        }

        final int getEst() {
            int s; // force initialization
            final LinkedList<E> lst;
            if ((s = est) < 0) {
                if ((lst = list) == null)
                    s = est = 0;
                else {
                    expectedModCount = lst.modCount;
                    current = lst.first;
                    s = est = lst.size;
                }
            }
            return s;
        }

        public long estimateSize() {
            return (long) getEst();
        }

        public Spliterator<E> trySplit() {
            Node<E> p;
            int s = getEst();
            if (s > 1 && (p = current) != null) {
                int n = batch + BATCH_UNIT;
                if (n > s)
                    n = s;
                if (n > MAX_BATCH)
                    n = MAX_BATCH;
                Object[] a = new Object[n];
                int j = 0;
                do {
                    a[j++] = p.item;
                } while ((p = p.next) != null && j < n);
                current = p;
                batch = j;
                est = s - j;
                return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
            }
            return null;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            Node<E> p;
            int n;
            if (action == null) throw new NullPointerException();
            if ((n = getEst()) > 0 && (p = current) != null) {
                current = null;
                est = 0;
                do {
                    E e = p.item;
                    p = p.next;
                    action.accept(e);
                } while (p != null && --n > 0);
            }
            if (list.modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

        public boolean tryAdvance(Consumer<? super E> action) {
            Node<E> p;
            if (action == null) throw new NullPointerException();
            if (getEst() > 0 && (p = current) != null) {
                --est;
                E e = p.item;
                current = p.next;
                action.accept(e);
                if (list.modCount != expectedModCount)
                    throw new ConcurrentModificationException();
                return true;
            }
            return false;
        }

        public int characteristics() {
            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
        }
    }

}

7.2 关注重点

7.2.1 增删改查

可以看到,Linkedlist 底层是双向链表,所以我们对Linkedlist 的增删改查,实际就是对双向链表的增删改查

7.2.2 equals

可以看到,可以调用Linkedlist 提供的remove方法等删除元素,其实底层会遍历list,逐个比较元素对象的equals方法来确定删除元素的位置。为此,如果要调用这个方法,需要确定equals 方法是期待的元素对象的比较逻辑,如果不是,需要重写equals 方法

7.2.3 线程安全

LinkedList 本身是线程不安全的。如果需要保证线程安全,可以通过集合类包装一层使用List list = Collections.synchronizedList(new LinkedList(...)),也可以使用线程安全的类
ConcurrentLinkedQueue 或者ConcurrentLinkedDeque

7.2.4 迭代器

java中的集合接口实现类,基本都实现了迭代器方法,所以我们可以通过通用的迭代器方法 next hasnext等通用方法,实现遍历不同的底层集合结构。这里实际上是迭代器设计模式的体现。

7.2.5 modcount

我们可以在源码很多地方看到modcount使用,即修改次数。在对list做结构性调整,比如扩容,缩容等操作时,次数会加一。最后我们在使用迭代器遍历时,创建的迭代器会保存期待的修改次数expectedModCount,遍历的时候会判断这个次数和我们预期的次数是不是一致,如果不一致说明有其他线程修改了list。如果不一致,LinkedList 会直接抛出ConcurrentModificationException异常,(Fail-Fast)快速失败。

7.2.6 实现接口

通过看LinkedList源码可以看到,里面关于元素的操作,有很多方法底层是重复的,比如push方法调用了addFirst()方法,addfirst方法又调用了linkFirst方法,这三个方法代码逻辑完全一样,但是写了三遍,这是因为linkedList 实现了Deque(双端队列),所以需要实现Deque接口设计的方法。 从方法名可以看出,因为Linkedlist实现了Deque ,那么也可以作为栈或者队列使用。 关于栈和队列更多的介绍,后面专栏会有提到。

以上,本人菜鸟一枚,如有错误,请不吝指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/555615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CryoEM - 冷冻电镜 EMPIAR 数据集的下载过程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/130812925 EMPIAR: Electron Microscopy Public Image Archive&#xff0c;电镜公开图像存档。 IBM Aspera Connect 是一款高效的文件传…

如何在两个月内考过软考高级

如何在两个月内考过软考高级 前言本人情况备考经历一些备考关键点考试中结果相关资料获取 前言 高级软考的作用这里不用多说&#xff0c;本人在2022年9月初开始备考&#xff0c;在11月初顺利通过高级系统架构师&#xff0c;期间的经历这里与大家分享一下。本人之前并没有考过其…

使用Jmeter连接MySQL测试实战

01、连接MQSQL数据库 1、jmeter要连接mysql数据库 首先得下载mysql jdbc驱动包&#xff0c;尽量保证其版本和你的数据库版本一致&#xff0c;至少不低于数据库版本&#xff0c;否则可能有问题。 官网下载地址为&#xff1a;https://dev.mysql.com/downloads/connector/j/ 下…

数据结构课程设计——哈夫曼编/译码器

数据结构课程设计任务书 学生姓名&#xff1a; 专业班级&#xff1a;软件工程 指导教师&#xff1a; 工作单位&#xff1a; 题 目: 哈夫曼编/译码器 基础要求&#xff1a; &#xff08;1&#xff09;熟悉各种…

HTML- 标签学习之- 表单

input 系列&#xff0c; 类型根据type 区分&#xff0c;所有效果如下&#xff1a; 注意点&#xff1a; 单/多选框&#xff1a; name &#xff1a; 相同name属性的单选值为一组&#xff0c;遗嘱中只能有一个被选中 checked&#xff1a; 默认选中 性别<i…

stm32的IIC驱动0.96OLED

IIC原理介绍&#xff1a; IIC是一个总线的结构但不支持总线协议 OLED介绍&#xff1a; 一、0.96寸OLED屏幕介绍 本文采用的是4针的0.96寸OLED显示进行讲解&#xff0c;采用的是SPI协议&#xff0c;速度会比采用I2C协议的更快&#xff0c;但这两者的显示驱动都一样&#xf…

AIGC功能在线制作思维导图?

ProcessOn思维导图软件是一款功能强大的在线制作思维导图的工具&#xff0c;它提供了丰富的模板和图标&#xff0c;可以帮助用户快速制作出高质量的思维导图。其中&#xff0c;AIGC(人工智能图形识别)功能是 ProcessOn软件中的一大特色&#xff0c;它可以帮助用户更加高效地制…

permiere的字幕自动转录功能

permiere2022.3后就能够离线字幕转录了&#xff0c;这个是个很好的消息&#xff08;但也要温馨的提示大家&#xff0c;这个功能对于非标注发音或者环境嘈杂的环境下的语音识别很不友好&#xff0c;当然&#xff0c;如果你想利用它来识别歌词&#xff0c;那就乘早死了这条心&…

chatgpt赋能Python-pythonslam

Pythonslam&#xff1a;实现SLAM技术的Python库 在机器人领域&#xff0c;SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;技术是非常重要的。SLAM技术使得机器人能够在未知环境中构建地图并同时确定自己的位置。然而&#xff0c;SLAM算法往往需要强大的计…

Godot引擎 4.0 文档 - 第一个 2D 游戏

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a; Your first 2D game — Godot Engine (stable) documentation in English 第一个 2D 游戏 在这个循序渐进的教程系列中&#xff0c;您将使用 Godot 创建您的第一个完…

上交清华搞事情!发起最全学科大模型中文知识及推理评测!GPT-4 竟然血洗所有国产模型

夕小瑶科技说 原创 作者 | 小戏&#xff0c;Python 从 OpenAI 的 ChatGPT、Meta 的 LLaMA、Anthropic 的 Claude 到复旦的 Moss、清华的 ChatGlm、MiniMax 的 Glow&#xff0c;国内的国外的大模型百花齐放层出不穷。那么&#xff0c;抛出一个相信大家都会关心的问题&#xff…

网狐大联盟服务端源码分析之服务核心-ServiceCore

工程属性分析: 1.工程属性->动态链接库 2.dll类型->MFC共享DLL 3.字符集->Unicode 4.库导出类型->使用模块定义文件def 5.生成的导出模块函数与对应的地址定义lib文件 源码分析: 头文件分析: 头文件与对应含义表示如下:

开关电源PCB排版基本规则

开关电源PCB排版是开发电源产品中的一个重要过程。许多情况下&#xff0c;一个在纸上设计得非常完美的电源可能在初次调试时无法正常工作&#xff0c;原因是该电源的PCB排版存在着许多问题。 为了适应电子产品飞快的更新换代节奏&#xff0c;产品设计工程师更倾向于选择在市场…

Linux安装部署Redis6.2.5图文教程

Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库&#xff0c;并提供多种语言的 API。最近学习需要用到Redis&#xff0c;所以就去Linux服务器上部署一个&#xff0c;做下记录&#xff0c;方便…

PostgreSQL中的行锁

行锁在PG中比较特殊&#xff0c;在9.4以前&#xff0c;只有两种类型的行锁&#xff0c; FOR UPDATE 和FOR SHARE&#xff0c;因为只有两种锁&#xff0c;粒度比较大&#xff0c;极大的影响了并发性。所以从9.4开始引入了FOR KEY SHARE和FOR NO KEY UPDATE这两种行锁。目前这四种…

防火墙——SNAT和DNAT策略的原理及应用、防火墙规则的备份、还原和抓包

防火墙—SNAT和DNAT策略的原理及应用、防火墙规则的备份、还原和抓包 一、SNAT策略概述1、SNAT应用环境2、SNAT原理3、SNAT转换的前提条件 二、SNAT策略的应用1、临时打开2、永久打开3、SNAT转换1&#xff1a;固定的公网IP地址4、SNAT转换2&#xff1a;非固定的公网IP地址&…

一文搞懂,PO设计模式详解

PO模式&#xff1a; 全称&#xff1a;page objece&#xff0c;分层机制&#xff0c;让不同层去做不同类型的事情&#xff0c;让代码结构清晰&#xff0c;增加复⽤性。 PO模式的优势&#xff1a; 1&#xff09;效率⾼ &#xff1a;同理&#xff0c;PO模式的逻辑层⽅法有具体定…

Flink第六章:多流操作

系列文章目录 Flink第一章:环境搭建 Flink第二章:基本操作. Flink第三章:基本操作(二) Flink第四章:水位线和窗口 Flink第五章:处理函数 Flink第六章:多流操作 文章目录 系列文章目录前言一、分流1.侧输出流(process function) 二、合流1. 联合&#xff08;Union&#xff09;2…

CVE-2018-2894WebLogic未授权任意文件上传

CVE-2018-2894WebLogic未授权任意文件上传 这个洞的限制就比较多了 限制版本 Oracle WebLogic Server版本 10.3.6.0 12.1.3.0 12.2.1.2 12.2.1.3 限制配置 该漏洞的影响模块为web服务测试页&#xff0c;在默认情况下不启用。 /ws_utc/config.do /ws_utc/begin.do 默认情况下不…

在职字节6年,一个29岁女软件测试工程师的心声

简单的先说一下&#xff0c;坐标杭州&#xff0c;14届本科毕业&#xff0c;算上年前在字节跳动的面试&#xff0c;一共有面试了有6家公司&#xff08;因为不想请假&#xff0c;因此只是每个晚上去其他公司面试&#xff0c;所以面试的公司比较少&#xff09; 其中成功的有4家&a…