如何理解Iterator
从最简单的用Iterator遍历ArrayList的一段程序开始
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next() + “\t”);
}
打印结果:
a b c d
上面的程序通过调用ArrayList对象list的iterator()方法获取了一个迭代器iterator,通过iterator的hasNext()方法和next()方法配合完成对list的遍历。
这种遍历方式与传统的按下标遍历是不太一样的
以上面的程序为例,当还未开始进行遍历时,迭代器的位置与list元素位置关系如下,iterator站在了首元素前面
开始遍历时,先调用hasNext()判断下一个要遍历的位置是否超过最后一个位置d,如果不超过,则调用next(),next()干了两件事情:
-
再次判断iterator的下一个位置是否超过d,超过的话则抛异常
-
iterator向前走一步,走到a和b之间的位置:
-
返回iterator的前一个位置,也就是a
这是遍历的第一步,接下来的每一步也是要走这样的流程。
所以,我们可以想象成,iterator处于两个元素之间的位置,每次遍历时,要用hasNext()先判断后面还有没有元素,有的话,则通过next()前进一步并返回上一个经过的元素
Iterator的remove方法
从一段错误的程序开始
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
iterator.remove();
}
这段程序妄图通过一直调用iterator的remove方法去删除整个list,但很遗憾它是一段错误的程序。
为了解释为什么,我们需要从ArrayList实现iterator()的源码找原因
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
//其他代码...
//ArrayList所实现的iterator方法
public Iterator<E> iterator() {
return new Itr();//返回内部类Itr
}
//内部类Itr
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回的元素的下标
int lastRet = -1; // 上一个返回的元素的下标;如果没有的话是-1
int expectedModCount = modCount;//校验版本号
// prevent creating a synthetic constructor
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]; //返回上一个遍历过的的元素,并更新lastRet的值
}
public void remove() {
if (lastRet < 0) //如果上一个返回的元素下标为-1则抛异常
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); //移除iterator上一个遍历过的元素
cursor = lastRet; //回退一个位置
lastRet = -1; //重设为-1
expectedModCount = modCount; //更新校验版本号
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int size = ArrayList.this.size;
int i = cursor;
if (i < size) {
final Object[] es = elementData;
if (i >= es.length)
throw new ConcurrentModificationException();
for (; i < size && modCount == expectedModCount; i++)
action.accept(elementAt(es, i));
// update once at end to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}
//校验版本号的函数
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
总结一下上面这段源码的思路
每次调用remove()时,都会先判断lastRet的值是否为-1,如果是,则抛异常
remove将删除lastRet位置的元素,也就是iterator"左边"那个元素,并将iterator的位置回退一格,修改lastRet=-1
因此,不可连续调用remove,不然会因为lastRet等于-1而抛异常
而配合next()使用,在next()中会更新lastRet和cursor的值,并在hasNext()中用cursor判断iterator之后是否还有元素
所以,修改后的程序为:
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
iterator.next();
iterator.remove();
}
Iterable与iterator的关系
Iterable接口中有一个方法iterator(),返回Iterator类型的对象,从Iterable接口到ArrayList类这条链来看,直到ArrayList才实现了iterator()方法:
从源码中可以看到,ArrayList对象返回的Iterator对象,其实是ArrayList对象中的一个实现了Iterator接口的内部类Itr的对象,用代码简而言之:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
public Iterator<E> iterator() {
return new Itr();
}
//Itr内部类
private class Itr implements Iterator<E> {
//具体实现...
}
}
Iterator的另一种遍历方式:forEachRemaining方法
使用forEachRemaining方法,也可以实现对集合的遍历
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
Iterator iterator = list.iterator();
iterator.forEachRemaining(element->{
System.out.println(iterator.next());
});
以上是我对Iterator接口的理解,若有不当之处,请大家批评指正!