直接上代码:
public class test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0 ; i < 10 ; i++ ) {
list.add(i);
}
Iterator<Integer> iterator = list.iterator();
int i = 0 ;
while(iterator.hasNext()) {
if (i == 3) {
iterator.remove(); //报java.lang.IllegalStateException异常
}
i ++;
}
System.out.println(list);
}
}
为什么会报异常呢,通过一些查资料或者基础较好的读者知道只需要使用调用迭代器iterator.next()方法即可返回当前元素,所以只需要在remove()方法前面加上
iterator.next();
注意这里有人会使用int x = iterator.next();这样来把当前的索引赋值然后再进行操作,其实这样是没必要的,我们来根据这个例子深入的了解一下iterator的源码是如何实现的。
首先我们调用的是list.iterator()方法来获取这个迭代器,当然list类并没有实现这个方法,我们最终是使用它的子类ArrayList来获取迭代器:
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
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();
}
}
可以看到,iterator()方法返回了一个Itr对象,接着下面,Itr是一个内部类,并且实现了Itertor接口。
回到我们最初的问题,为什么在迭代器中不用在不使用next()方法情况下进行remove操作会出错?我们来看下remove()方法源码,
在remove方法第一行,即可看到:
if (lastRet < 0)
throw new IllegalStateException();
当lastRet < 0的情况下,会抛出IllegalStateException异常,那么这个lastRet是什么呢,其实在Itr类就定义了这个lastRet变量,和它一起的还有cursor,expectedModCount;
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;
通过注释可以看到,lastRet就是上个元素的索引,默认是-1,所以直接调用迭代器的remove()方法会报错就是这个原因,
所以在上面的next()方法里,我们可以看到这样的代码:
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];
它会把cursor游标赋值给i,然后进行+1操作,最后将lastRet赋值成i,所以lastRet就不是默认的-1了,而是会得到上个元素的索引,
所以综合上述的结论,在使用迭代器iterator.remove()方法前,一定先调用next()方法来给变量lastRet进行赋值操作,
还有读者或许会问,为什么要使用迭代器的remove()方法,而不是使用集合自身的remove(),add()等方法呢,可以阅读我的另一片博客。