博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Effective Java学习(通用程序设计)之——for-each循环优先于传统的for循环
阅读量:4151 次
发布时间:2019-05-25

本文共 2782 字,大约阅读时间需要 9 分钟。

在java1.5发行之前,对于集合进行遍历的首选做法如下:

 

 

for(Iterator i = c.iterator(); i.hasNext();){    i.next();}

 

 

遍历数组的首先做法是:

 

for(int i = 0; i < a.length; i++){    a[i];}

 

 

      这些做法都比while循环更好,但是他们并不完美。迭代器和索引变量都会造成一些混乱。而且,他们也代表着出错的可能。迭代器和索引变量在每个循环中出现三次,其中有两次让你很容易出错,一旦出错,就无法保证编译器能够发现错误。

 

      java1.5之后引入了for-each循环,通过完全隐藏的迭代器或者索引变量,避免了混乱和出错的可能,这种模式同样使用与集合和数组

 

for(Element e : elements){   }

       当你到冒号 :时,可以把他读作“在.....里面“。因此上面的循环可以读作”对于元素中的每一个元素e"注意,利用for-each循环不会有性能损失,甚至用于数组也是一样,实际上,在某些情况下,比起普通的for循环,他还稍微有些性能优势,因为他对数组索引的边界值只计算一次。虽然可以手工完成这项工作,但是程序员并不总是这么做。

 

 

      在对多个集合进行嵌套循环迭代时,for-each循环相对于普通的for循环优势会更加明显,下面就是试图对两个集合进行嵌套循环时经常会犯的错误:

 

enum Suit{CLUB,DIAMOND,HEART,SPADE}enum Rank{ACE,DEUCE,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN,JACK,QUEEN,KING}Collection
suits = Arrays.asList(Suit.values()); Collection
ranks = Arrays.asList(Rank.values()); List
deck = new ArrayList
(); for(Iterator
i = suits.iterator(); i.hasNext();){ for(Iterator
j = ranks.iterator(); j.hasNext();){ deck.add(new Card(i.next(),j.next())); }}public class Card { public Card(Suit next, Rank next2) { System.out.println(next+" " + next2); }}

      如果之前没有发现bug也不必难过,许多专家级的程序员偶尔也会犯这样的错误。问题在于,在这个迭代器上对于外部的集合调用了太多次的next方法了,他应该从外部的循环进行调用,以便于每种花色调用一次,但它却是从内部循环调用的,因此他是每张牌调用一次。在用完所有花色之后,循环就会抛出NullPointerException异常。

 

 

       如果真的那么不幸,并且外部集合的大小是内部集合大小的几倍——可能因为它们是相同的集合——循环就会正常终止,但是不会完成你想要的工作,例如:下面是个考虑不周的尝试,要打印一对骰子的所有 可能的滚动:

 

enum Face{ONE,TWO,THREE,FOUR,FIVE,SIX}Collection
faces = Arrays.asList(Face.values()); for(Iterator
i = faces.iterator(); i.hasNext();){ for(Iterator
j = faces.iterator(); j.hasNext();){ System.out.println(i.next()+ " " + j.next()); } }

 这段程序不会抛出任何异常,而是打印出6对重复的词(从ONE ONE到SIX SIX)而不是预计的36中组合。

 

 

         为了修正这个bug,必须在外部循环的作用域中添加一个变量来保存外部元素:

 

for(Iterator
i = suits.iterator(); i.hasNext();){ Suit suit = i.next(); for(Iterator
j = ranks.iterator(); j.hasNext();){ deck.add(new Card(suit,j.next())); }}

 如果使用的是嵌套的for-each循环,这个问题就会完全消失。产生的代码就如你所希望得到的那样简洁:

 

 

for (Suit suit : suits) {	for(Rank rank : ranks){		deck.add(new Card(suit, rank));	}}

         for-each循环不仅让你遍历集合和数组,还让你遍历任何实现Iterable接口的对象。这个简单的接口有单个方法组成,与for-each循环同时被添加到java平台,下面就是这个接口的示例:

 

 

public interface Iterable
{ Iterable
iterator();}

       实现Iterable接口并不能,如果你在编写的类型表示的是一组元素,即使你选择不让他实现Collection,也要让它实现Iterable接口,这样可以允许用户利用for-each循环遍历你的类型,会令用户永远感激不尽的。

 

 

       总之 ,for-each循环在简洁性和放bug方面有着传统for循环无法比拟的优势,并且没有性能损失,应该尽可能的使用for-each循环。遗憾的是,有三种常见的情况布恩无法使用for-each循环:

 

过滤 ——如果需要遍历集合,并删除选定的元素,就需要使用显示的迭代器,比便于可以调用他的remove方法。

 

转换——如果需要遍历的列表或者数组,并取代他部分或者全部的元素值,就需要列表迭代器或者数组索引,以便设定元素的值。

 

平行迭代——如果需要并行的遍历多个集合,就需要显示的控制迭代器或者索引变量,以便所有迭代器或者索引变量都可以得到同步前移。

 

在以上任何一种情况下,就要使用普通的for循环,要警惕本条目中提到的陷阱,并且要确保做到最好。

转载地址:http://eilti.baihongyu.com/

你可能感兴趣的文章
SVG 形状学习之——SVG圆形
查看>>
SVG 滤镜学习之——SVG 滤镜
查看>>
mysql中用命令行复制表结构的方法
查看>>
hbase shell出现ERROR: org.apache.hadoop.hbase.ipc.ServerNotRunningYetException
查看>>
让代码变得更优雅-Lombok
查看>>
解决Rhythmbox乱码
查看>>
豆瓣爱问共享资料插件发布啦
查看>>
Ubuntu10.10 CAJView安装 读取nh\kdh\caj文件 成功
查看>>
kermit的安装和配置
查看>>
vim 配置
查看>>
openocd zylin
查看>>
进程创建时文件系统处理
查看>>
进程创建时信号处理函数处理
查看>>
进程创建时信号处理
查看>>
进程创建时内存描述符处理
查看>>
进程创建时命名空间处理
查看>>
进程创建时IO处理
查看>>
进程创建时线程栈处理
查看>>
进程创建时pid分配
查看>>
进程创建时安全计算处理
查看>>