之前看完了LinkedList中的一些基本方法,这一篇再看看稍微复杂一点的方法。首先放一张LinkedList的继承关系图。
对于List来说,使用最多的操作估计就是遍历了,遍历操作一般都是使用循环。当然在1.8之后 Iterable 接口新增了一个 forEach 方法,由于LinkedList及其父类都没有实现这个方法,所以调用 LinkedList·的forEach方法就是默认的实现,使用增强FOR循环来遍历,然后对每一个元素执行一个action操作。
// 省略其他方法
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
我们都知道,增强FOR循环本质上都是使用的迭代器来遍历,接着我们再来看看LinkedList的迭代器。获取迭代器有三个方法可以获取,分别是来自 Iterable 接口的 iterator 方法作为通用的迭代器;来自 List 接口的两个 listIterator 方法,用作针对List设计的迭代器,功能更多。
// 省略其他方法
public interface List<E> extends Collection<E> {
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
}
尽管有三个方法可以获取到迭代器,但是LinkedList的迭代器却只有一个。
// 省略其他方法
public abstract class AbstractSequentialList<E> extends AbstractList<E> {
public Iterator<E> iterator() {
return listIterator();
}
public abstract ListIterator<E> listIterator(int index);
}
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public ListIterator<E> listIterator() {
return listIterator(0);
}
}
在 LinkedList 的父类中可以看到,最终调用的都是 listIterator(index)方法。现在我们再去 LinkedList 中看看这个方法的实现。
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();
}
}
显然,LinkedList 内部实现了一个私有的 ListIterator ,构造函数接收一个index作为参数,通过node(index)方法来找到迭代的起点。在来看看内部属性:
lastReturned:上一次返回的节点;
next:下一个要遍历的节点;
nextIndex:下一个要遍历的节点位置;
expectedModCount:初始化等于modCount,为了标识在遍历过程中除了当前迭代器外 LinkedList 没有其他的增删改操作。
再看方法实现:
hasNext:判断是否还有节点未遍历;
next:返回下一个遍历的节点。先判断链表是否被其他线程改过,再判断是否还有未遍历的节点。再将 lastReturned 指向 next,next指向next.next,nextIndex++,最后返回原next.item。
hasPrevious:判断当前节点是否有前一个节点;
previous:返回前一个节点;和next方法类似,区别在于next指向了next.prev,nextIndex--。而且,lastReturned和next指向的是同一个节点。
nextIndex:返回当前节点的下一个位置;
previousIndex:返回当前节点的前一个位置;
remove:从链表中删除上一个返回的节点;先判断链表是否被其他线程改过,再判断上一个返回的节点是否为null;先把lastReturned.next保存一份,再将lastReturned节点从链表中删除,由于previous方法使得lastReturned和next指向的是同一个节点,所以next == lastReturned 表示之前是从后向前迭代的,所以需要把next指向lastReturned.next;如果是从前向后迭代的,那么只需要将nextIndex--即可,即下一个要遍历的元素位置还是当前位置,最后维护下lastReturned和expectedModCount。
set:更新上一个返回的节点的值;
add:添加元素到链表中;如果已经到了尾部则添加到最后,否则添加到下一个要遍历的节点之前;
forEachRemaining:遍历剩余节点;对下一个要遍历的节点一直到尾部的节点,全部执行一次action操作。对每个节点的操作都需要判断modCount和expectedModCount;
遍历的操作看完之后还有截取的操作,主要是 subList(fromIndex,toIndex)方法。这个方法来自父类AbstarctList,LinkedList并未实现,在我的另外一篇 文章 中对这个方法已经读过一遍了,这里就不再看了。
到这里LinkedList的一些源码基本都读的差不多了,等有时间了再去研究一些难度较高的源码吧。