代码1:
public class MainFunction {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("aaa");//1
stringList.add("bbb");//2
ListIterator<String> stringListIterator = stringList.listIterator();
stringListIterator.add("ccc");//3
stringListIterator.add("ddd");//4
stringListIterator.next();//5
stringListIterator.add("eee");//6
}
}
代码2:
public class MainFunction {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
// stringList.add("aaa");
// stringList.add("bbb");.
ListIterator<String> stringListIterator = stringList.listIterator();
stringListIterator.add("ccc");//3
stringListIterator.add("ddd");//4
stringListIterator.next();//5
stringListIterator.add("eee");//6
}
}
代码1和代码2几乎是相同的,唯一不同得地方是代码2注解了两行代码。分别执行代码1和代码2,代码1正常执行,代码2抛出了异常:NoSuchElementException
分析一下为什么代码2会执行出错:
先看看next方法源码中的注解:
/**
* Returns the next element in the list and advances the cursor position.
* This method may be called repeatedly to iterate through the list,
* or intermixed with calls to {@link #previous} to go back and forth.
* (Note that alternating calls to {@code next} and {@code previous}
* will return the same element repeatedly.)
*
* @return the next element in the list
* @throws NoSuchElementException if the iteration has no next element
*/
E next();
我们可以在next方法注解的第一句看到“返回迭代器中的下一个元素,并且向前移动光标”,这里有两个东西我们需要去搞懂:
1、什么是“下一个元素”
2、什么是光标
在ArrayList类中有一个内部类实现了Iterator接口,它的源码如下:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
看完源码我们就能大概的明白:光标是一个存储集合中元素下标的变量(cursor),所谓的下一个元素就是光标指向的这个元素。迭代器中光标的默认值是0,且只有next方法会改变光标的值,add方法在光标指向的前一个位置添加元素。
代码1和代码2执行过程时集合中元素的变化大概是这样的:(左边为代码2,右边为代码1)
代码2中由于光标指向的位置没有元素,所以调用next方法时会报错。