Java集合 List实现类 LinkedList (双链表) 源码浅析

版权声明:转载请注明出处 https://blog.csdn.net/weixin_39554102/article/details/84990315

Java集合 List实现类 LinkedList 源码浅析

LinkedList源码实现基于 jdk 1.8

源代码来源于 jdk 1.8

一 、简述(来自JAVA api 注释)

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 getremoveinsert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

此类实现 Deque 接口,为 addpoll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。

注意,此实现不是同步的。 如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:

  List list = Collections.synchronizedList(new LinkedList(...));

此类的 iteratorlistIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 removeadd 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

此类是 Java Collections Framework 的成员。

类图
在这里插入图片描述

此处只探讨List 方法的实现, 对于 Queue 和 Deque 队列的实现,稍后有专门的章节探讨.

点击此处 Queue 和 Deque 实现

二、构造方法

LinkedList() 和 LinkedList(Collection<? extends E> c) ;

	// 构造一个空列表。
    public LinkedList() {
    }
    /**
    * 构造一个包含指定 collection 中的元素的列表,
    * 这些元素按其 collection 的迭代器返回的顺序排列。
    */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

三、List方法

在分析源码前,先看看LinkedList 是如何存储元素的?
LinkedList 里有一个内部类, Node (节点) ;

类型 / 字段 描述
E item 泛型, 存放着添加进去的元素
Node next 存放下一个节点的指针
Node prev 存放上一个节点的指针

LinkedList 里有两个属性 first 和 last

first 指向第一个节点的指针
last 指向最后一个节点的指针

由下图可以看出LinkedList 是双链表, 并且是不循环的链表.此链表的特性是内存利用率高, 删除和添加元素快.可以向前搜索, 也可以向后搜索
链表图示

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

1、add(E e)

源码分析: 声明一个记录尾节点的指针 l, l = last;新建一个节点 new Node<>(l, e, null), prev = last, next = null,
即前一个指针指向原来的尾节点, 下一个节点为null; 如果为节点l为null, 头节点等于尾节点; 如果不为null,原来的尾节点的下一个节点的指针指向当前新键的节点 newNode.
时间复杂度为 O(1)

	// 将指定元素添加到此列表的结尾。
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    // 链接e作为最后一个元素。
    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++;
    }

2、add(int index, E element)

源码注释: 将指定元素插入此列表中的指定位置。 将当前位置的元素(如果有)和任何后续元素向右移动(将其添加到索引中)。

源码解析: 添加元素前, 检查索引是否越界, 如果越界跑错异常;
如果: 索引等于链表长度, 则添加到链表的尾部;
如果: 索引小于链表长度,则先获取当前索引的节点, 在把元素添加到这个节点的前面;

时序图
在这里插入图片描述

    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
    // 检查索引是否有效,否则抛出 IndexOutOfBoundsException 异常
    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    // 判断参数是否是迭代器或添加操作的有效位置的索引。
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }
    // 构造一个下标越界的异常信息,(java.lang.IndexOutOfBoundsException: Index: 44, Size: 3)
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }
    // 返回指定元素索引处的(非null)节点。
    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;
        }
    }
    // 在非null节点succ之前插入元素e。
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

3. 获取指定元素 get(int index)

源码注释: 返回此列表中指定位置的元素。

源码分析: 先检查元素索引, 然后调用 node() 获取指定索引的元素;
获取元素的算法为遍历链表, 时间复杂度为 O(n)

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    // 检查元素索引, 如果索引超出范围 (index < 0 || index >= size())
    // 则抛出异常 
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    // 判断参数是否是现有元素的索引。
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

4. 替换元素 set(int index, E element)

源码注释: 用指定的元素替换此列表中指定位置的元素。

源码分析: 检查索引, 调用 node() 方法获取指定位置的节点, 然后替换节点的元素 x.item = element;

    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

5.移除元素 remove(int index)

源码注释: 删除此列表中指定位置的元素。 将任何后续元素向左移位(从索引中减去一个元素)。 返回从列表中删除的元素。

源码分析: 首先检查元素索引, 然后调用 node 获取指定索引的节点, 再调用 unlink 断开这个节点;
unlink 会判断这个节点是否为首节点first 或是尾节点 last

在这里插入图片描述

    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    // 断开非空节点 x
    // 这个方法的操作大致分三段, 输入需要断开的节点 x,获取 x节点的前一个指针/下一个节点指针/当前元素指针
    // 1.判断前一个节点指针是否为null,如果为true, 即节点x 就是首节点 first
    // 2.判断下一个节点指针是否为null, 如果为true, 即节点x 就是尾节点 last
    // 3.当前元素指针置空, size--
    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;
    }

6.移除指定元素 remove(Object o)

源码注释: 从该列表中删除指定元素的第一个匹配项(如果存在)。 如果此列表不包含该元素,则不会更改。 更正式地,删除具有最低索引i的元素,使得 (o == null ? get(i)==null : o.equals(get(i))) (如果存在这样的元素)。 如果此列表包含指定的元素,则返回true(或等效地,如果此列表因调用而更改)。

源码分析: 代码的逻辑 , 则迭代链表,使用参数 o与每个元素比较, 找到最早匹配到的元素, 最后调用 unlink() 方法断开节点

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

7.链表的元素数目 size()

源码注释: 返回此列表中的元素数。

    public int size() {
        return size;
    }

8. 判断是否为空 isEmpty()

源码注释: 如果此collection不包含任何元素,则返回true。
此实现返回 size() == 0

    public boolean isEmpty() {
        return size() == 0;
    }

9.清空链表元素 clear()

源码注释: 从此列表中删除所有元素。 此调用返回后,列表将为空。

源码分析: 迭代链表把每个节点的指针置空, 此操作后 size = 0, 首节点和尾节点指针为null, modCount 计数器 +1

    public void clear() {
		 //清除节点之间的所有链接是“不必要的”,但是:
         //  - 如果丢弃的节点居住,则帮助生成GC
         //不止一代
         //  - 即使有可达的迭代器,也一定要释放内存
        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++;
    }

10.查找指定元素索引 indexOf(Object o)

源码注释: 返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。
更正式地,返回最低索引i,使得 (o==null ? get(i)==null : o.equals(get(i))) ,如果没有这样的索引则返回-1。

源码分析: 此处的逻辑与 remove(Object o) 方法的逻辑一样, 只是最后一步的操作有差异.此处最后一步是返回匹配到的元素索引

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

11.判断是否包含指定元素 contains(Object o)

源码注释: 如果此列表包含指定的元素,则返回true。 更正式地,当且仅当此列表包含至少一个元素e时才返回true
(o == null ? e == null : o.equals(e))

源码分析: 这个方法是indexOf() 方法的扩展版本;

    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

12.是否包含指定集合的所有元素 containsAll(Collection<?> c)

源码注释: 如果此collection包含指定collection中的所有元素,则返回true。
此实现迭代指定的集合,依次检查迭代器返回的每个元素以查看它是否包含在此集合中。 如果包含所有元素,则返回true,否则返回false。

源码分析: 首先迭代参数集合c, 使用集合c的每个元素作为contains()方法的参数, 搜索元素.如果出现一次 false, 马上返回, 即链表不包含这个集合c的所有元素;
这个时间复杂度可以理解为 O(n^2), 因为此处是两个for循环嵌套

    public boolean containsAll(Collection<?> c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }

13.删除包含指定集合的元素 removeAll(Collection<?> c)

源码注释: 删除此集合的所有元素,这些元素也包含在指定的集合中(可选操作)。 此调用返回后,此集合将不包含与指定集合相同的元素。
此实现迭代此集合,依次检查迭代器返回的每个元素,以查看它是否包含在指定的集合中。 如果包含它,则使用迭代器的remove方法将其从此集合中删除。
请注意,如果迭代器方法返回的迭代器未实现remove方法,并且此collection包含与指定collection相同的一个或多个元素,则此实现将抛出UnsupportedOperationException

源码分析: 先判断c是否非空, 再获取当前链表的迭代器, 调用集合c 的 contains()方法搜索迭代器返回的元素it.next;如果返回true就调用迭代器的 remove()方法移除这个元素;
由此可知, 假若获取的迭代器不支持 remove方法,则抛出异常 UnsupportedOperationException

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }

14.链表转数组 toArray()

源码注释: 以适当的顺序(从第一个元素到最后一个元素)返回包含此列表中所有元素的数组。

返回的数组将是“安全的”,因为此列表不会保留对它的引用。 (换句话说,此方法必须分配一个新数组)。 因此调用者可以自由修改返回的数组。

此方法充当基于阵列和基于集合的API之间的桥梁。

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

15.链表转数组, 返回运行时类型 toArray(T[] a)

源码注释: 以适当的顺序返回包含此列表中所有元素的数组(从第一个元素到最后一个元素);返回数组的运行时类型是指定数组的运行时类型。如果列表适合指定的数组,则返回其中。否则,将为新数组分配指定数组的运行时类型和此列表的大小。

如果列表适合指定的数组,并且有空余空间(即,数组的元素多于列表),则紧跟在列表末尾的数组中的元素将设置为null。 (仅当调用者知道列表不包含任何null元素时,这在确定列表长度时很有用。)

与toArray()方法一样,此方法充当基于数组的API和基于集合的API之间的桥梁。此外,该方法允许精确控制输出阵列的运行时类型,并且在某些情况下可以用于节省分配成本。

假设x是已知仅包含字符串的列表。以下代码可用于将列表转储到新分配的String数组中:

   String [] y = x.toArray(new String [0]);

请注意,toArray(new Object [0]) 在功能上与toArray() 相同。

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

16.排序sort()

源码注释: 根据指定的比较器引发的顺序对此列表进行排序。

此列表中的所有元素必须使用指定的比较器进行相互比较(即,c.compare(e1,e2) 不得对列表中的任何元素e1和e2抛出ClassCastException)。

如果指定的比较器为null,则此列表中的所有元素都必须实现Comparable接口,并且应使用元素的自然顺序。

此列表必须是可修改的,但无需调整大小。

源码分析: 这个方法带有 default 关键字, 是 List 接口里的默认方法
源码逻辑, 输入一个比较器Comparator; 先调用 toArray() 获取一个新数组 a, 再排序数组a;
然后获取当前链表的迭代器, 再遍历a,把a 的每个值重新设置到链中, 因为 set()方法不会修改链表的结构, 所以不会抛出 ConcurrentModificationException
这个排序的效率关键在于 Arrays.sort() 方法的时间复杂度

    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

17. hashCode() 与 equals(Object o)

hashCode() 源码注释: 返回此列表的哈希码值。

此实现完全使用用于在List.hashCode方法的文档中定义列表哈希函数的代码。

    public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

equals 源码注释:
将指定对象与此列表进行比较以获得相等性。 当且仅当指定的对象也是列表时,返回true,两个列表具有相同的大小,并且两个列表中的所有对应元素对都相等。 (如果 (e1 == null ? e2 == null : e1.equals(e2)).) 则两个元素e1和e2相等。)换句话说,如果两个列表包含相同顺序的相同元素,则它们被定义为相等。

此实现首先检查指定的对象是否为此列表。 如果是,则返回true; 如果不是,它检查指定的对象是否是列表。 如果不是,则返回false; 如果是这样,它迭代两个列表,比较相应的元素对。 如果任何比较返回false,则此方法返回false。 如果迭代器在另一个之前耗尽了元素,则返回false(因为列表的长度不等); 否则在迭代完成时返回true。

源码逻辑: 判断列表是否相等的条件, 大小相同, 顺序相同, 里面的元素必须满足 (o1 == null ? o2 == null : o1.equals(o2))

    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }

扩展讨论:
这里有一个比较有趣的现象, 我们看看 eclipse 自动生成的 equals 和 hashcode 函数
为什么, 运算hash 值要 使用 31 作为基数?
据说是31为素数, 这主要利用了素数的数学特性. 比如只能被1 和 自己整除, 能够尽可能地避免了哈希冲突

public class HQExample {

	private int hash;

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + hash;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		HQExample other = (HQExample) obj;
		if (hash != other.hash)
			return false;
		return true;
	}
}

克隆clone() 与序列化 readObject() and writeObject()

a) clone()

源码注释: 返回此LinkedList的浅表副本。 (元素本身未被克隆。)

    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;
    }
        
    private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

b) readObject()

源码注释: 从流中重构此LinkedList实例(即反序列化它)。

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

c) writeObject()

源码注释: 将此LinkedList实例的状态保存到流(即将其序列化)。
@serialData
列表的大小(它包含的元素的数量)被发出(int),然后是正确顺序的所有元素(每个都是一个Object)。

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

四、迭代器 Iterator

1. 获取迭代器 iterator()

源码注释: 返回此列表中元素的迭代器(按正确顺序)。此实现仅返回列表上的列表迭代器。

源码分析: 此处逻辑可知, iterator()返回的迭代器和listIterator()返回的迭代器都是 ListItr 的实例, 因此都是可以向前和向后遍历的

    public Iterator<E> iterator() {
        return listIterator();
    }

2. 获取 listIterator()

源码注释: 返回此列表中元素的列表迭代器(按适当顺序)。此实现返回 listIterator(0)。

    public ListIterator<E> listIterator() {
        return listIterator(0);
    }

3. 指定索引的 listIterator(int index)

源码注释: 从列表中的指定位置开始,返回此列表中元素的列表迭代器(按正确顺序)。 遵守List.listIterator(int )的一般合约。

list-iterator是快速失败的:如果在创建Iterator之后的任何时候对列表进行结构修改,除了通过list-iterator自己的remove或add方法之外,list-iterator将抛出ConcurrentModificationException。 因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险。

    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

4.迭代器的实现类 ListItr

类图
在这里插入图片描述
ListItr 源码

源码分析: ListItr 实现了 Iterator 和ListIterator ,

 private class ListItr implements ListIterator<E> {
 		// 最后返回的节点
        private Node<E> lastReturned;
        // 下一个节点
        private Node<E> next;
        // 下一个索引
        private int nextIndex;
        // 并发修改的标记
        private int expectedModCount = modCount;
		// 初始化时, lastReturned = null, next = node(index)(这个索引的节点, 比如0,就是首节点)
		// nextIndex = index, expectedModCount = modCount(即这个链表被修改过的次数)
        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }
}

可以调用的方法有 hasNext()、next、remove()、hasPrevious()、previous()、add(E e)、set(E e)

方法 说明
hasNext() 如果仍有元素可以迭代,则返回 true。
next() 返回迭代的下一个元素。
remove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
hasPrevious() 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
previous() 返回列表中的前一个元素。
add(E e) 将指定的元素插入列表(可选操作)。
set(E e) 用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。

hasNext()

源码注释: 如果此列表迭代器在向前遍历列表时具有更多元素,则返回true。 (换句话说,如果next会返回一个元素而不是抛出异常,则返回true。)

        public boolean hasNext() {
            return nextIndex < size;
        }

next()

源码注释: 返回列表中的下一个元素并前进光标位置。 可以重复调用此方法以遍历列表,或者与之前的调用混合以来回传递。 (注意,对next和previous的交替调用将重复返回相同的元素。)

        public E next() {
            checkForComodification();
            if (!hasNext())
                throw new NoSuchElementException();

            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.item;
        }

hasPrevious()

源码注释: 如果此列表迭代器在反向遍历列表时具有更多元素,则返回true。(换句话说,如果previous先前返回一个元素而不是抛出异常,则返回true。)

        public boolean hasPrevious() {
            return nextIndex > 0;
        }

previous()
源码注释: 返回列表中的上一个元素并向后移动光标位置。 可以重复调用此方法以向后遍历列表,或者与来回调用混合以来回来。 (注意,对next和previous的交替调用将重复返回相同的元素。)

        public E previous() {
            checkForComodification();
            if (!hasPrevious())
                throw new NoSuchElementException();

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

remove()
源码注释: 从列表中删除next或previous返回的最后一个元素(可选操作)。 此呼叫只能在每次呼叫下一次或上一次时进行一次。 只有在最后一次调用next或previous之后才调用add时,才能进行此操作。

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

set(E e)

源码注释: 用指定的元素替换next或previous返回的最后一个元素(可选操作)。 只有在最后一次调用next或previous之后才调用remove和add时,才能进行此调用。

        public void set(E e) {
            if (lastReturned == null)
                throw new IllegalStateException();
            checkForComodification();
            lastReturned.item = e;
        }

add(E e)

源码注释: 将指定的元素插入列表(可选操作)。 元素将紧接在next将返回的元素之前插入(如果有),并且在之前返回的元素之后插入(如果有)。 (如果列表中不包含任何元素,则新元素将成为列表中的唯一元素。)新元素在隐式游标之前插入:对next的后续调用不受影响,后续调用previous将返回新元素。 (此调用将调用nextIndex或previousIndex返回的值增加1。)

        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null)
                linkLast(e);
            else
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

其他方法:

		// 返回对 next 的后续调用所返回元素的索引。
        public int nextIndex() {
            return nextIndex;
        }
		// 返回对 previous 的后续调用所返回元素的索引。
        public int previousIndex() {
            return nextIndex - 1;
        }
        // 对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。 
        // 这个方法在 Iterator里的默认实现的方法
        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();
        }

扩展阅读

ArrayList 实现 ArrayList 列表实现浅析
LinkedList 实现 LinkedList 双链表实现浅析

2018-12-13 持续更新…

猜你喜欢

转载自blog.csdn.net/weixin_39554102/article/details/84990315
今日推荐