详解JAVA中的for-each循环与迭代
在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为"foreach"语句的目标,而此接口中的唯一方法,实现的就是返回一个在一组T类型的元素上进行迭代的迭代器。
一、迭代器Iterator
接口:Iterator<T>
publicinterfaceIterator<E>{ booleanhasNext(); Enext(); voidremove(); }
查看Iterator接口API可以知道,这是对collection进行迭代的迭代器。迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的collection移除元素。
尤其值得注意的是此迭代器remove()方法的使用:从迭代器指向的collection中移除迭代器返回的最后一个元素(可选操作)。每次调用next只能调用一次此方法。如果进行迭代时用调用此方法(remove方法)之外的其他方式修改了该迭代器所指向的collection,则迭代器的行为是不确定的。接口设计人员在设计Iterator<T>接口的时候已经指出,在进行迭代时如果调用了除了迭代器的remove()方法修改了该迭代器所指向的collection,则会造成不确定的后果。具体出现什么后果依迭代器的具体实现而定。针对这种不确定的后果可能出现的情况,在学习ArrayList时遇到了其中一种:迭代器抛出ConcurrentModificationException异常。具体异常情况如下代码所示:
importjava.util.ArrayList; importjava.util.Collection; importjava.util.Iterator; publicclassItaratorTest{ publicstaticvoidmain(String[]args){ Collection<String>list=newArrayList<String>(); list.add("Android"); list.add("IOS"); list.add("WindowsMobile"); Iterator<String>iterator=list.iterator(); while(iterator.hasNext()){ Stringlang=iterator.next(); list.remove(lang);//willthrowConcurrentModificationException } } }
此段代码在运行时会抛出ConcurrentModificationException异常,因为我们在迭代器运行期间没有用iterator的remove()方法来删除元素,而是使用ArrayList的remove()方法改变了迭代器所指向的collection。这就违反了迭代器的设计原则,所以发生了异常。
所报异常情况如下所示:
Exceptioninthread"main"java.util.ConcurrentModificationException
atjava.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
atjava.util.ArrayList$Itr.next(ArrayList.java:831)
atText.ItaratorTest.main(ItaratorTest.java:17)
二、for-each循环与迭代器Iterator<T>
从Java5起,在Java中有了for-each循环,可以用来循环遍历collection和array。Foreach循环允许你在无需保持传统for循环中的索引,或在使用iterator/ListIterator(ArrayList中的一种迭代器实现)时无需调用while循环中的hasNext()方法就能遍历collection。for-each循环简化了任何Collection或array的遍历过程。但是使用foreach循环也有两点需要注意。
使用foreach循环的对象,必须实现了Iterable<T>接口
请看如下示例:
importjava.util.ArrayList; publicclassForeachTest1{ publicstaticvoidmain(Stringargs[]){ CustomCollection<String>myCollection=newCustomCollection<String>(); myCollection.add("Java"); myCollection.add("Scala"); myCollection.add("Groovy"); //Whatdoesthiscodewilldo,printlanguage,throwexceptionor //compiletimeerror for(Stringlanguage:myCollection){ System.out.println(language); } } privateclassCustomCollection<T>{ privateArrayList<T>bucket; publicCustomCollection(){ bucket=newArrayList(); } publicintsize(){ returnbucket.size(); } publicbooleanisEmpty(){ returnbucket.isEmpty(); } publicbooleancontains(To){ returnbucket.contains(o); } publicbooleanadd(Te){ returnbucket.add(e); } publicbooleanremove(To){ returnbucket.remove(o); } } }
上述代码将无法通过编译,这是因为代码中的CustomCollection类没有实现Iterable<T>接口,编译期的报错如下:
Exceptioninthread"main"java.lang.Error:Unresolvedcompilationproblem:
Canonlyiterateoveranarrayoraninstanceofjava.lang.Iterable
atText.ForeachTest1.main(ForeachTest1.java:15)
事实上,无需等到编译时才发现报错,eclipse会在这段代码写完之后就会在foreach循环处显示错误:Canonlyiterateoveranarrayoraninstanceofjava.lang.Iterable
从上述示例可以再次得到确认的是,foreach循环只适用于实现了Iterable<T>接口的对象。由于所有内置Collection类都实现了java.util.Collection接口,已经继承了Iterable,所以为了解决上述问题,可以选择简单地让CustomCollection实现Collection接口或者继承AbstractCollection。解决方式如下:
importjava.util.AbstractCollection; importjava.util.ArrayList; importjava.util.Iterator; publicclassForeachTest{ publicstaticvoidmain(Stringargs[]){ CustomCollection<String>myCollection=newCustomCollection<String>(); myCollection.add("Java"); myCollection.add("Scala"); myCollection.add("Groovy"); for(Stringlanguage:myCollection){ System.out.println(language); } } privatestaticclassCustomCollection<T>extendsAbstractCollection<T>{ privateArrayList<T>bucket; publicCustomCollection(){ bucket=newArrayList(); } publicintsize(){ returnbucket.size(); } publicbooleanisEmpty(){ returnbucket.isEmpty(); } publicbooleancontains(Objecto){ returnbucket.contains(o); } publicbooleanadd(Te){ returnbucket.add(e); } publicbooleanremove(Objecto){ returnbucket.remove(o); } @Override publicIterator<T>iterator(){ //TODOAuto-generatedmethodstub returnbucket.iterator(); } } }
2.foreach循环的内部实现也是依靠Iterator进行实现的
为了验证foreach循环是使用Iterator作为内部实现这一事实,我们依然采用本文最开始的实例进行验证:
publicclassItaratorTest{ publicstaticvoidmain(String[]args){ Collection<String>list=newArrayList<String>(); list.add("Android"); list.add("IOS"); list.add("WindowsMobile"); //example1 //Iterator<String>iterator=list.iterator(); //while(iterator.hasNext()){ //Stringlang=iterator.next(); //list.remove(lang); //} //example2 for(Stringlanguage:list){ list.remove(language); } } }
程序运行时所报异常:
Exceptioninthread"main"java.util.ConcurrentModificationException
atjava.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
atjava.util.ArrayList$Itr.next(ArrayList.java:831)
atText.ItaratorTest.main(ItaratorTest.java:22)
此异常正说明了for-each循环内部使用了Iterator来遍历Collection,它也调用了Iterator.next(),这会检查(元素的)变化并抛出ConcurrentModificationException。
总结:
- 在遍历collection时,如果要在遍历期间修改collection,则必须通过Iterator/listIterator来实现,否则可能会发生“不确定的后果”。
- foreach循环通过iterator实现,使用foreach循环的对象必须实现Iterable接口
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。