for循环是写代码最常用的,但是之前看过《Effective java》,有些场景不建议写。结合我之前刷算法题的经验,让我受益匪浅。1、for循环的缺点以往遍历元素时,我们通常采用如下形式:publicclassMain{publicstaticvoidmain(String[]args){//1,数组元素int[]num=newint[]{1,2,3,4,5};//数组遍历for(inti=0;ilists=newArrayList<>();lists.add(newPerson("张三"));lists.add(newPerson("李四"));lists.add(newPerson("愚公想movemountains"));//对象遍历元素for(Iteratorit=lists.iterator();it.hasNext();){Personp=it.next();System.out.println(p.getName());}}}This这种写法看起来不错,但并不完美。下面我们来分析一下有哪些缺点。然后给出解决方案。问题一:迭代器或索引多次出现,容易造成使用错误。从上面两段遍历代码来看,数组元素是通过索引i遍历的,但是整个for循环出现了四次i,而对象元素是通过迭代器it遍历的,但是整个for循环出现了三次.当for循环遍历元素时,有很多机会使用错误的变量。而有时这些错误编译器是找不到的。对整个应用系统造成不可预知的错误。问题二:遍历对象元素时,需要注意容器类型。比如我们这里用的是list,当然也可能有其他的容器类型。这些类型改起来比较麻烦。问题三:嵌套迭代抛出异常的情况比较复杂,先打个例子吧。比如我们要列出每一朵花,这些花有两个属性,一个是颜色,一个是大小。publicclassMain{//枚举颜色和大小enumColor{RED,GREEN,BLUE,BLACK}enumSize{ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN}//定义花staticclassFlower{publicFlower(Colorcolor,Sizesize){}}publicstaticvoidmain(String[]args){Collectioncolors=Arrays.asList(Color.values());Collectionsizes=Arrays.asList(Size.values());Listflowers=newArrayList<>();//for循环添加所有花朵和尺寸for(Iteratorcolor=colors.iterator();color.hasNext();){for(Iteratorsize=sizes.iterator();size.hasNext();){flowers.add(newFlower(color.next(),size.next()));}}}}看来人畜无害,现在我们运行一个海浪。Exceptioninthread"main"java.util.NoSuchElementExceptionatjava.util.AbstractList$Itr.next(UnknownSource)atcom.f2.Main.main(Main.java:25)是不是有点奇怪,好像双循环没什么问题遍历,但是出现原因是外部的Color迭代器被调用了多次,第一层for循环调用了,但是在第二层for循环内部调用了,所以调用了color的next。所以有NoSuchElementException。但有时这不会发生。场景是调用外层循环迭代器的次数恰好是内层调用的n倍。问题四:嵌套迭代没有抛出异常,但是结果不正确。在这种情况下,外层循环迭代器的调用次数恰好是内层调用的n倍。再举个例子:publicclassMain{//enumerationcolorenumColor{RED,GREEN,BLUE,BLACK}publicstaticvoidmain(String[]args){Collectioncolors=Arrays.asList(Color.values());//二for循环层for(Iteratorc1=colors.iterator();c1.hasNext();){for(Iteratorc2=colors.iterator();c2.hasNext();){System.out.println(c1.next()+""+c2.next());}}}}现在对颜色进行for循环遍历,一共两层for循环,因为一共有四种颜色,两层for循环应该是打印16个结果。现在再次运行它以查看结果:REDREDGREENGREENBLUEBLUEBLACKBLACK没错,它确实打印了四个。原因同第三个问题。有一种方法可以很好地解决这个嵌套问题。嵌套迭代问题解决:直接看代码。既然是外部迭代器,又是内部使用的,那如果我在内部和外部之间用一个变量来缓存就好了。publicclassMain{//枚举颜色enumColor{RED,GREEN,BLUE,BLACK}publicstaticvoidmain(String[]args){Collectioncolors=Arrays.asList(Color.values());//for循环for(Iteratorc1=colors.iterator();c1.hasNext();){//使用一个变量来缓存colorc=c1.next();for(Iteratorc2=colors.iterator();c2.hasNext();){System.out.println(c+""+c2.next());}}}}现在再次运行,可以很好的得到16种结果。这种方法也比较好,但是不能很好的解决问题1和2。所以,为了解决这个现象,老板JoshuaBloch在书中提出,建议使用for-each循环,而不是for循环。二、for-each循环既然作者推荐使用for-each循环,那么我们来看看它有什么好处。如何解决以上问题。publicclassMain{//枚举颜色和大小enumColor{RED,GREEN,BLUE,BLACK}enumSize{ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN}//定义花staticclassFlower{publicFlower(Colorcolor,Sizesize){}}publicstaticvoidmain(String[]args){Collectioncolors=Arrays.asList(Color.values());Collectionsizes=Arrays.asList(Size.values());Listflowers=newArrayList<>();//for-each循环for(Colorcolor:colors){for(Sizesize:sizes){flowers.add(newFlower(color,size));}}}}在里面寻找-每个循环。以上问题全部解决。好吧,也许你会觉得,仅此而已?还有一个我还没有提到的好处,让我们继续阅读。for-each循环不仅允许遍历集合和数组,还允许遍历任何实现Iterable接口的对象,它由一个方法组成。接口定义如下:publicinterfaceIterable{//ReturnsaniteratorovertheelementsinthisiterableIteratoriterator();}表示元素的集合,那么你应该强烈考虑让它实现Iterable接口,甚至选择不实现Collection接口。这允许用户使用for-each循环迭代类型,他们将永远感激这一点。但是,常见的三种情况不能单独使用for-each循环:(1)破坏性过滤:如果需要遍历集合,删除选中的元素,需要使用显式的迭代器,这样就可以调用其删除方法。通常可以通过Java8新增的Collection类中的removeIf方法来避免显式遍历。(2)转换:如果需要遍历一个列表或数组,并替换其元素的部分或全部值,那么你需要一个列表迭代器或者数组索引来替换元素的值。(3)并行迭代:如果需要并行遍历多个集合,需要显式控制迭代器或索引变量,使所有迭代器或索引变量同步。如果您发现自己处于这些情况中的任何一种,请使用传统的for循环并警惕本文中提到的陷阱。本文转载自微信公众号“愚公要移山”,可通过以下二维码关注。转载本文请联系愚公移山公众号。