一、介绍 双向链表实现了 List
和 Deque
接口。它实现了所有可选的列表操作,并允许所有类型的元素(包括 null)。所有操作的性能都符合对双向链表的预期。对于那些需要索引列表的操作,链表会根据指定索引靠近列表起点或终点的情况,从起点或终点开始遍历。
需要注意的是,这个实现并不是线程安全的 。如果多个线程同时访问一个链表,并且至少有一个线程对链表进行了结构性修改,则必须通过外部同步来确保线程安全。结构性修改指的是添加或删除一个或多个元素的操作;仅仅设置元素的值不属于结构性修改。通常的做法是对封装链表的某个对象进行同步。如果没有这样的对象存在,链表应当使用 Collections.synchronizedList
方法进行“包装”。最好在链表创建时进行包装,以防止意外的非同步访问:
1 List list = Collections.synchronizedList(new LinkedList (...));
这个类的 iterator
和 listIterator
方法返回的迭代器是快速失败的(fail-fast):如果在迭代器创建后,链表被结构性修改了(除了通过迭代器自身的 remove
或 add
方法进行的修改外),迭代器会抛出 ConcurrentModificationException
。因此,在并发修改的情况下,迭代器会迅速而干净地失败,而不是在未来的不确定时间内面临任意的、不可预测的行为。
需要注意的是,迭代器的快速失败行为无法得到保证,因为在未同步的并发修改情况下,一般来说无法做出任何硬性保证。快速失败的迭代器是在“尽力而为”的基础上抛出 ConcurrentModificationException
。因此,编写依赖于该异常来确保正确性的程序是错误的做法:迭代器的快速失败行为应仅用于检测程序中的错误。
二、源码 1 2 3 public class LinkedList <E> extends AbstractSequentialList <E> implements List <E>, Deque<E>, Cloneable, java.io.Serializable { }
1、基础节点 1 2 3 4 5 6 7 8 9 10 11 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; } }
LinkedList 中定了内部类 Node,用来表示链表中的节点。提供属性如下:
item
,节点所存储数据
next
,下一个节点
prer
,上一个节点
2、属性 1 2 3 4 5 transient int size = 0 ;transient Node<E> first; transient Node<E> last;
size
,当前链表中存储的元素数量
first
,链表中的第一个节点
last
,链表中的最后一个节点
3、构造方法 1 2 3 4 5 6 7 public LinkedList () { } public LinkedList (Collection<? extends E> c) { this (); addAll(c); }
相较于 ArrayList
,LinkedList
没有提供入参为初始化容量的构造方法,相应的方法在 ArrayList
中为 public ArrayList(int initialCapacity)
。
4、实现 Deque 接口方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void addFirst (E e) ;void addLast (E e) ;boolean offerFirst (E e) ;boolean offerLast (E e) ;E removeFirst () ; E removeLast () ; E pollFirst () ; E pollLast () ; E getFirst () ; E getLast () ; E peekFirst () ; E peekLast () ; boolean removeFirstOccurrence (Object o) ;boolean removeLastOccurrence (Object o) ;
关于双端队列 Deque
的内容,请参考 [[Deque]]
4.1 addFirst 该方法在此链表的开头插入指定的元素。
1 2 3 public void addFirst (E e) { linkFirst(e); }
4.2 addLast 该方法在链表的结尾插入指定元素。
1 2 3 public void addLast (E e) { linkLast(e); }
4.3 offerFirst 1 2 3 4 public boolean offerFirst (E e) { addFirst(e); return true ; }
4.4 offerLast 1 2 3 4 public boolean offerLast (E e) { addLast(e); return true ; }
4.5 removeFirst 删除并返回此列表中的第一个元素。
1 2 3 4 5 6 7 public E removeFirst () { final Node<E> f = first; if (f == null ) { throw new NoSuchElementException (); } return unlinkFirst(f); }
4.6 removeLast 删除并返回此列表中的最后一个元素。
1 2 3 4 5 6 7 public E removeLast () { final Node<E> l = last; if (l == null ) { throw new NoSuchElementException (); } return unlinkLast(l); }
4.7 pollFirst 检索并删除此列表的第一个元素,如果此列表为空,则返回 null。
1 2 3 4 public E pollFirst () { final Node<E> f = first; return (f == null ) ? null : unlinkFirst(f); }
4.8 pollLast 检索并删除此列表的最后一个元素,如果此列表为空,则返回 null。
1 2 3 4 public E pollLast () { final Node<E> l = last; return (l == null ) ? null : unlinkLast(l); }
4.9 getFirst 返回此列表中的第一个元素。
1 2 3 4 5 6 7 public E getFirst () { final Node<E> f = first; if (f == null ) { throw new NoSuchElementException (); } return f.item; }
4.10 getLast 返回此列表中的最后一个元素。
1 2 3 4 5 6 7 public E getLast () { final Node<E> l = last; if (l == null ) { throw new NoSuchElementException (); } return l.item; }
4.11 peekFirst 检索但不删除此列表的第一个元素,如果此列表为空,则返回 null。
1 2 3 4 public E peekFirst () { final Node<E> f = first; return (f == null ) ? null : f.item; }
4.12 peekLast 检索但不删除此列表的最后一个元素,如果此列表为空,则返回null。
1 2 3 4 public E peekLast () { final Node<E> l = last; return (l == null ) ? null : l.item; }
5、实现 Queue 接口方法 1 2 3 4 5 6 7 8 9 10 11 boolean add (E e) ;boolean offer (E e) ;E remove () ; E poll () ; E element () ; E peek () ;
5.1 add 将指定元素附加到此列表的末尾。此方法等效于 addLast
。
1 2 3 4 public boolean add (E e) { linkLast(e); return true ; }
5.2 offer 将指定元素添加为此列表的尾部(最后一个元素)。
1 2 3 public boolean offer (E e) { return add(e); }
5.3 remove 检索并删除此列表的头部(第一个元素)。
1 2 3 public E remove () { return removeFirst(); }
5.4 poll 检索并删除此列表的头部(第一个元素)。
1 2 3 4 public E poll () { final Node<E> f = first; return (f == null ) ? null : unlinkFirst(f); }
5.5 element 检索但不删除此列表的头部(第一个元素)。
1 2 3 public E element () { return getFirst(); }
5.6 peek 检索但不删除此列表的头部(第一个元素)。
1 2 3 4 public E peek () { final Node<E> f = first; return (f == null ) ? null : f.item; }
6、实现 Collection 接口方法 6.1 contains 如果此列表包含指定元素,则返回 true
。
更正式地说,当且仅当此列表包含至少一个元素 e
满足 Objects.equals(o, e)
时,返回 true
。
1 2 3 public boolean contains (Object o) { return indexOf(o) >= 0 ; }
6.2 toArray 方法 1 2 3 Object[] toArray(); <T> T[] toArray(T[] a);
6.2.1 Object[] toArray();
方法 返回一个包含此列表中所有元素的数组,元素按正确的顺序排列(从第一个到最后一个元素)。
返回的数组是“安全的”,因为此列表不会保留对该数组的引用。(换句话说,此方法必须分配一个新的数组)。因此,调用者可以自由地修改返回的数组。
此方法在基于数组的 API 和基于集合的 API 之间起到桥梁作用。
1 2 3 4 5 6 7 8 9 10 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; }
6.2.2 <T> T[] toArray(T[] a);
方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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; }
6.3 add Java 集合框架中的 LinkedList 类 | z2huo
6.4 remove 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 ; }
6.5 addAll 方法 将指定集合中的所有元素按其迭代器返回的顺序追加到此列表的末尾。
如果在操作进行过程中修改了指定的集合,则此操作的行为是未定义的。注意,如果指定的集合是此列表自身并且它非空,则会发生这种情况。
1 2 3 public boolean addAll (Collection<? extends E> c) { return addAll(size, c); }
6.6 containAll 方法 继承自 AbstractCollection
抽象类中的 containAll
方法
6.7 removeAll 方法 继承自 AbstractCollection
抽象类中的 removeAll
方法
6.8 retainAll 方法 继承自 AbstractCollection
抽象类中的 retainAll
方法
6.9 clear 方法 删除此列表中的所有元素。此调用返回后,列表将为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void clear () { for (Node<E> x = first; x != null ; ) { Node<E> next = x.next; x.item = null ; x.next = null ; x.prev = null ; x = next; } first = last = null ; size = 0 ; modCount++; }
7、实现 List 接口方法 7.1 搜索元素操作 7.1.1 indexOf 返回指定元素在此列表中首次出现的索引,如果此列表不包含该元素,则返回 -1。
更正式地说,返回满足 Objects.equals(o, get(i))
的最低索引 i
,如果没有这样的索引,则返回 -1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 ; }
7.1.2 lastIndexOf 返回指定元素在此列表中最后一次出现的索引,如果此列表不包含该元素,则返回 -1。
更正式地说,返回满足 Objects.equals(o, get(i))
的最高索引 i
,如果没有这样的索引,则返回 -1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 ; }
lastIndexOf
方法内部逻辑与 indexOf
大体相同,前者从后往前遍历链表,后者从前往后遍历链表。
7.2 addAll 方法 boolean addAll(int index, Collection<? extends E> c)
方法将指定集合中的所有元素插入到此列表中的指定位置。从该位置开始的元素(如果有)以及所有后续元素将向右移动(它们的索引增加)。新元素将按照指定集合的迭代器返回的顺序出现在列表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 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; Node<E> 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 ; }
7.3 get 方法 返回此列表中指定位置的元素。
1 2 3 4 public E get (int index) { checkElementIndex(index); return node(index).item; }
7.4 set 方法 将此列表中指定位置的元素替换为指定元素。
1 2 3 4 5 6 7 8 public E set (int index, E element) { checkElementIndex(index); Node<E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; }
7.5 add 方法 在此列表中的指定位置插入指定元素。将当前位于该位置的元素(如果有的话)和任何后续元素向右移动(将其索引加一)。
1 2 3 4 5 6 7 8 9 10 11 public void add (int index, E element) { checkPositionIndex(index); if (index == size) { linkLast(element); } else { linkBefore(element, node(index)); } }
7.6 remove 方法 删除此列表中指定位置的元素。将任何后续元素向左移动(从其索引中减去一)。返回从列表中删除的元素。
1 2 3 4 public E remove (int index) { checkElementIndex(index); return unlink(node(index)); }
8、迭代器 iterator()
方法继承自 AbstractSequentialList
抽象类,其中调用了 listIterator()
方法。
1 2 3 public Iterator<E> iterator () { return listIterator(); }
8.1 列表迭代器 LinkedList
中提供了返回列表迭代器的方法。
1 2 3 4 public ListIterator<E> listIterator (int index) { checkPositionIndex(index); return new ListItr (index); }
另外还有继承自 AbstractList
的返回列表迭代器的方法。
1 2 3 public ListIterator<E> listIterator () { return listIterator(0 ); }
LinkedList
中的列表迭代器 ListItr
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 private class ListItr implements ListIterator <E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; private int expectedModCount = modCount; ListItr(int 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 (); } }
8.2 逆序迭代器 1 2 3 public Iterator<E> descendingIterator () { return new DescendingIterator (); }
LinkedList
类提供了一个方法 descendingIterator()
,它返回一个以逆序遍历列表元素的迭代器。该迭代器会从列表的最后一个元素开始,依次向前遍历到第一个元素。
逆序迭代器的源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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(); } }
9、逆序视图 LinkedList
类中的 reversed()
方法返回此集合的逆序视图。返回视图中的元素顺序与此集合中的元素顺序相反。逆序排列会影响所有依赖顺序的操作,包括对返回视图的视图集合的操作。如果集合的实现允许对该视图进行修改,则这些修改会“传递”到底层集合中。根据具体实现,对底层集合的更改可能会或可能不会在该逆序视图中可见。
对逆序视图的修改是允许的,并且这些修改将传播到此列表。此外,对该列表的修改也将在逆序视图中可见。
1 2 3 4 5 6 public LinkedList<E> reversed () { return new ReverseOrderLinkedListView <>(this , super .reversed(), Deque.super .reversed()); }
LinkedList
类的内部类 ReverseOrderLinkedListView
定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 static class ReverseOrderLinkedListView <E> extends LinkedList <E> implements java .io.Externalizable { final LinkedList<E> list; final List<E> rlist; final Deque<E> rdeque; ReverseOrderLinkedListView(LinkedList<E> list, List<E> rlist, Deque<E> rdeque) { this .list = list; this .rlist = rlist; this .rdeque = rdeque; } }
10、基础支持方法 10.1 节点链接操作 10.1.1 linkFirst 链接入参中的 e 节点作为链表的第一个元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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++; }
10.1.2 linkLast 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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++; }
10.1.3 unlinkFirst 取消链接非空的第一个节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private E unlinkFirst (Node<E> f) { final E element = f.item; final Node<E> next = f.next; f.item = null ; f.next = null ; first = next; if (next == null ) { last = null ; } else { next.prev = null ; } size--; modCount++; return element; }
10.1.4 unlinkLast 取消链接非空的最后一个节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private E unlinkLast (Node<E> l) { final E element = l.item; final Node<E> prev = l.prev; l.item = null ; l.prev = null ; last = prev; if (prev == null ) { first = null ; } else { prev.next = null ; } size--; modCount++; return element; }
10.1.5 unlink 取消链接入参中指定的非空节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 E unlink (Node<E> x) { 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; }
10.1.6 linkBefore 在入参非空节点 succ
之前插入元素e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void linkBefore (E e, Node<E> succ) { 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++; }
10.2 索引定位方法 Node<E> node(int index)
方法返回指定元素索引处的(非空)节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Node<E> node (int 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; } }
11、关于栈的方法 双端队列还可以用作 LIFO(后进先出)栈。该接口应优先于传统的 Stack
类使用。当双端队列用作栈时,元素从双端队列的开头被推入或弹出。栈方法与双端队列方法等价,Stack
和 Deque
方法比较如下表所示:
Stack 方法
Deque 等价方法
push(e)
addFirst(e)
pop()
removeFirst()
peek()
getFirst()
1 2 3 4 5 6 7 8 9 10 11 12 public void push (E e) { addFirst(e); } public E pop () { return removeFirst(); } public E peek () { final Node<E> f = first; return (f == null ) ? null : f.item; }
12、removeFirstOccurrence 移除列表中首次出现的指定元素(当从头到尾遍历列表时)。如果列表中不包含该元素,则列表保持不变。
1 2 3 public boolean removeFirstOccurrence (Object o) { return remove(o); }
Java 集合框架中的 LinkedList 类 | z2huo
13、removeLastOccurrence 移除列表中最后一次出现的指定元素(当从头到尾遍历列表时)。如果列表中不包含该元素,则列表保持不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 ; }
上面的方法可以看做是 Java 集合框架中的 LinkedList 类 | z2huo 方法的翻版,不同的是 remove
方法为从前往后遍历,而 removeLastOccurrence
方法为从后往前遍历。
三、其他问题 1、ArrayList 与 LinkedList 的对比
底层数据结构
ArrayList
: 基于动态数组实现。它使用一个可调整大小的数组来存储元素。
LinkedList
: 基于双向链表实现。每个元素都是一个节点,节点包含指向前一个和后一个节点的引用。
存储方式
ArrayList
: 连续的内存块,用于存储所有元素。每次添加或删除操作可能会导致数组重新分配更大的空间。
LinkedList
: 每个元素是独立的节点,存储在非连续的内存中。每个节点包含元素本身以及指向前后节点的引用。
访问速度
ArrayList
: 由于其底层是数组,能够通过索引直接访问元素,时间复杂度为 O(1)
。适合频繁的随机访问操作。
LinkedList
: 访问某个元素需要从头或尾遍历链表,时间复杂度为 O(n)
。不适合频繁的随机访问。
插入和删除操作
ArrayList
: 插入或删除元素时,特别是对中间元素的操作,可能涉及到移动大量的元素,时间复杂度为 O(n)
。但是,末尾的插入操作 add(E e)
时间复杂度为 O(1)
。
LinkedList
: 插入和删除操作无需移动其他元素,时间复杂度为 O(1)
,但需要遍历链表找到插入或删除的位置,整体时间复杂度为 O(n)
。对于头尾的操作效率较高 addFirst
或 removeFirst
,时间复杂度为 O(1)
。
内存开销
ArrayList
: 数组在扩展时需要重新分配和复制,因此可能存在额外的内存开销。当数组容量超出当前大小时,会按照一定的倍数扩展。每个元素只存储它本身,没有额外的开销。
LinkedList
: 每个节点都需要额外的存储空间来保存前后节点的引用,因此内存开销较大。
适用场景
ArrayList
: 适合频繁读取和随机访问的场景,例如通过索引获取元素,或需要快速查找元素的场合。
LinkedList
: 适合频繁插入、删除元素的场景,尤其是在头尾插入或删除时效率更高。
线程安全 。ArrayList
和 LinkedList
默认都是非线程安全的。如果在多线程环境中使用,需要手动进行同步,例如使用 Collections.synchronizedList()
。
相关链接 迭代器 Iterator | z2huo
迭代器 ListIterator | z2huo
Deque | z2huo
Java 集合框架中的 LinkedList 类 | z2huo
OB links [[迭代器 Iterator]]
[[迭代器 ListIterator]]
[[Deque]]
[[AbstractCollection]]
#Java