(备注:本文基于JDK1.8)
LinkedList重写了listIterator()方法,父类AbstractList中定义了抽象方法listIterator()、AbstractList的父类AbstractSequentialList也定义了抽象方法listIterator()。listIterator()抽象方法是定义在List接口中的一个规范,它表示获得一个列表迭代器,LinkedList具体的实现了此listIterator()方法,它会返回具备双向遍历元素能力的迭代器对象,今天我们一起学习一下LinkedList中的双向迭代器!
ListItr类结构
private class ListItr implements ListIterator<E> {
………………省略很多源码………………
}
ListItr类定义在LinkedList类的内部(作为普通内部类),它实现了ListIterator接口。因为ListItr是普通内部类,它产生的每一个对象会隐式的持有每个外部类对象的引用……
ListItr类重要字段介绍
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
lastReturned 表示ListItr对象持有的,最后一次访问的元素对象
next 表示ListItr对象下一次即将访问的元素(假设迭代器第一次创建,并且我们设想最左到最右,此时的next的初始值为指向第一个元素,每当你调用它访问元素的方法,它返回的是就是next指向的元素)
nextIndex 表示ListItr对象下一次要访问的元素的下标
expectedModCount 表示ListItr持有的预期的修改次数,该字段专门用于判断是否有多个线程同时修改LinkedList持有的元素,看见那个modCount了吗?modCount是LinkedList对象持有的修改次数,此处能调用的原因是ListItr对象隐式持有外部类对象的引用!fail-fast机制必备!
ListItr功能介绍
1、改变元素数量功能
add():LinkedList对象中添加一个元素
remove():LinkedList对象中删除一个元素
2、向前遍历元素功能
hasPrevious():判断是否有上一个元素可访问
previous():获得上一个访问的元素
previousIndex():获得上一个访问的元素在线性表中的位置(第一个元素的位置是0)
3、向后遍历元素功能
hasNext():判断是否有下一个元素可访问
next():获得下一个访问的元素
nextIndex():获得下一个访问的元素在线性表中的位置(第一个元素的位置是0)
4、容错功能
checkForComodification() :防止多线程下修改LinkedList的元素
5、遍历元素功能
forEatchRemaining():遍历剩余元素(没有访问过的元素,就是剩余的元素)
PS:接下来逐个分析这些方法……
add()方法分析
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
用于向LinkedList中已遍历完的元素的前面添加一个元素
举个例子:当你遍历到第二个元素的时候,你调用此add(E)方法,此时新插入的元素将作为第二个元素,曾经的第二个元素将成为第三个元素!
1、检查是否发生并发修改,防止多线程下使用LinkedList
由checkforComodification()方法检查是否发生过多个线程下操作LinkedList的情况,只要发生过操作元素的行为,就会抛出一个ConcurrentModificationException异常对象
2、将迭代器最后一次访问元素的标记初始化为null
lastReturned指向最后一次访问的元素对象
3、针对空的LinkedList做处理
当ListItr对象持有的next为null,说明LinkedList并没有元素,因为ListItr对象创建时,持有的next会指向第一个元素,此时使用linkLast()方法完成元素的添加
4、对LinkedList非空的情况,将新增的元素添加到当前遍历元素的前面
通过调用linkBefore()方法完成添加工作,新增加的元素将成为当前即将遍历元素的前一个元素!
5、将下次访问元素的下标增加1
nextIndex增加1
6、fail-fast机制保护,防止多线程下使用LinkedList
将expectedModCount增加1
remove()方法分析
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++;
}
用于删除最后一次遍历过的元素
1、fail-fast机制保护
通过调用checkForComodification()方法
2、针对没有遍历过元素的情况……直接抛出异常警告用户
当lastReturned指向null,说明没有遍历过任何一个元素,此时抛出IllegalStateException对象告知用户错误行为
3、取出来当前遍历元素的下一个元素,由局部变量lastNext负责保存
4、调用unlink()方法删除最后遍历过的元素
5、什么情况最后一次遍历过的元素与即将遍历的元素会是同一个呢?
next == lastReturned?有点懵逼……
6、正常情况下,nextIndex减去1
7、更新最后遍历过的元素为空
lastReturned为null
8、fail-fast机制保护,将expectedModCount增加1
hasPrevious()方法分析
public boolean hasPrevious() {
return nextIndex > 0;
}
用于判断是否有上一个元素的方法
通过对ListItr对象持有的nextIndex值进行判断,当nextIndex值大于0时,则返回true,表示存在上一个元素
previous()方法分析
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
用于遍历上一个元素的方法
1、fail-fast检测,防止在多线程下使用ListItr遍历元素
调用checkForComodification()方法完成并发检测
2、检查是否有上一个元素
通过调用hasPrvious()方法检测是否有可以遍历的上一个元素,没有可遍历的元素时,会抛出NoSuchElementException对象
3、查找到本次需要遍历的节点对象,两种情况做出处理
第一种情况:已经遍历完所有元素
ListItr对象持有的lastReturned存储着最后一次返回的元素,如果next为null,说明已经遍历完最后一个元素,此时如果调用previous()方法,则是向前遍历元素,所以将LinkedList对象持有的实例变量last赋值给next、然后再将next值赋值给lastReturned,完成更新
另外一种情况:遍历到中间的元素
获取next指向节点对象持有的prev,prev表示上一个节点对象,赋值给next、以及next再赋值给lastReturned
4、更新元素的索引值
next减去1
5、返回节点对象持有的元素
返回lastReturned指向的节点对象持有的item
previousIndex()方法分析
public int previousIndex() {
return nextIndex - 1;
}
用于获取即将向左侧遍历的元素的索引值
hasNext()方法分析
public boolean hasNext() {
return nextIndex < size;
}
用于返回是否存在下一个可遍历元素的方法
nextIndex小于表示元素总数的size值,说明仍然有更多元素可以遍历,nextIndex与size值相等或者大于,就表示已经没有更多元素可以遍历了
next()方法分析
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
用于遍历下一个元素的方法,previous()方法是向左侧遍历、next()方法则是向右侧遍历
nextIndex()方法分析
public int nextIndex() {
return nextIndex;
}
用于表示返回下一个即将遍历元素的索引值
checkForComodification()方法分析
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
用于fail-fast机制检查的方法
当LinkedList对象持有的modCount值(定义在父类AbstractList中)与ListItr对象持有的expectedModCount值不相等时,说发生过并发修改,抛出ConcurrentModificationException异常对象,告知用户不能在多线程环境下使用LinkedList
forEachRemaining()方法分析
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();
}
用于遍历完所有剩余元素的方法,必须传入一个Consumer对象,Consumer对象中的accept()方法可以做什么工作呢?有机会用一次这个方法
总结
1、迭代器不仅遍历元素,还可以添加、删除元素
2、迭代器定义在LinkedList的内部,每个迭代器对象隐式持有LinkedList对象的引用