Java中Iterator(迭代器)与foreach学习
1.迭代器使用
Iterator(迭代器)给我们提供了一种遍历序列的方式,其中主要关于以下几种方法:
1.iterator()方法,返回一个Iterator对象。
2.next()方法,获取迭代器的下一个元素,并会向后移动一个单位(注:初次调用next()会返回序列中的第一个元素)
3.hasNext()方法,判断迭代器是否还有下一个元素(不会移动迭代器)
4.remover()方法,删除当前的迭代器所指向元素,一般与next()方法连用
Iterator遍历序列实施操作示例:
Iterator iter = subjects.iterator();//subjects为字符串序列 while(iter.hasNext()){ subject = iter.next(); if(subject.startsWith("6.")){//将以6.开头的字符串从序列中移除 iter.remove(); } }
foreach语句格式:
for(元素类型type 元素变量value : 遍历对象obj) {
引用value的语句;
}
2.foreach过程分析
foreach内部调由迭代器实现,但是当需要删除序列元素时,foreach循环会发生错误
如:
分析:
ConcurrentModificationException异常
Iterator next方法实现源码:
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]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
在next方法中首先调用了checkForComodification方法,该方法会判断modCount是否等于expectedModCount,不等于就会抛出java.util.ConcurrentModificationExcepiton异常。
注:modCount是ArrayList的一个属性,继承自抽象类AbstractList,用于表示ArrayList对象被修改次数(add、remove、clear、ensureCapacityInternal均会改变modCount值)。在创建Iterator的时候会将modCount赋值给expectedModCount,之后expectedModCount不再改变。设置该检查的目的是为了阻止程序员在不允许修改的时候修改对象,Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变。 当索引指针往后移动的时候就找不到要迭代的对象,所以禁止迭代时修改对象,于是会抛出错误。
在执行next方法时,将会检查modCount与expectedModCount值,由于foreach方法中,其内部由iterator实现,也会调用next()方法,但是,每次删除元素时均会改变modCount值,因此,调用序列自身的remove()方法后再次调用next()方法时,经检查modCount != expectedModCount,因此抛出ConcurrentModificationException异常
然而,在使用迭代器时,对迭代中的元素进行删除并不会抛出错误,原因是:在iterator.remove()方法中,同样调用了ArrayList自身的remove方法,但是调用完之后并非就return了,而是expectedModCount = modCount重置了expectedModCount值,使二者的值继续保持相等。同时,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性,不会导致索引指针移动时找不到迭代对象。
下面使用迭代器的语句同样会抛出ConcurrentModificationException异常:
Iterator iter = subjects.iterator(); while(iter.hasNext()){ subject = iter.next(); if(subject.startsWith("6.")){ subjects.remove(subject);//仅将iter.remove()更改为subjects.remove(subject) } }
可以看出,异常确实是由迭代器在迭代过程中抛出
参考: