其实我们在写代码的时候一直在使用for循环,但是偶尔还是纠结到底用哪个循环。一、for循环的基本1、使用while也是一种循环方式。这里我们将探索与for相关的循环,而不是展开它们。2、遍历数组时,初学者使用如下风格的for循环:for(inti=0;ioptions=Arrays.asList(Option.values());for(Iteratori=options.iterator();i.hasNext();){for(Iteratorj=options.iterator();j.hasNext();){System.out.println(i.next()+""+j.next());}但是执行之后你会发现这段代码有漏洞,结果只有四groups:那么剩下的组合去哪儿了?这里程序并没有抛出异常,只是因为i.next()每次都会取一个值,所以出现了上图的情况。但是,如果外层集合的元素大于内层元素:比如下面的代码:enumOptionFirst{Tom,Jerry,Jack,Mary}enumOptionSecond{Tom,Jerry,Jack,Mary,Mali,Tomsun,Lijie,Oolyyou}publicstaticvoidmain(String[]args){CollectionoptionFirstCollection=Arrays.asList(OptionFirst.values());CollectionoptionSecondCollection=Arrays.asList(OptionSecond.values());for(Iteratori=optionFirstCollection.iterator();i.hasNext();){for(Iteratorj=optionSecondCollection.iterator();j.hasNext();){System.out.println(i.next()+""+j.next());}}}运行后会抛出java.util.NoSuchElementException。原因是多次调用外层循环,内层循环因为元素不足抛出这样的异常。解决这个问题,只需要在第二次循环前加一个变量,用来保存外部元素即可;可以达到预期的效果。Collectionoptions=Arrays.asList(Option.values());for(Iteratori=options.iterator();i.hasNext();){Optionoption=i.next();for(Iteratorj=options.iterator();j.hasNext();){System.out.println(option+""+j.next());}}其次,for-each循环无论是数组还是集合都很实用,效率更高;expression形式更加简洁明了。for(Elemente:elements){System.out.println(e);}再次遇到上述二组问题时,就不用考虑元素不足的问题了,代码也简洁多了:for(Optionoption:options){for(Optionrank:options){System.out.println(option+""+rank);}}《Effective Java》像这样写for-each循环:3.for-each不是神说的-Each有那么多好处,却不是神,也不是万能的。需要注意三种情况。3.1.解构和过滤时不能使用。如果需要遍历集合,删除选中的元素,需要使用显式迭代器,这样才能调用它的remove方法。而Java8中加入的Collection的removeIf方法,往往可以避免显式遍历。例如,以下代码:Listlist=newArrayList();list.add("1");list.add("2");for(Stringitem:list){if("1".equals(item)){list.remove(item);System.out.println("if语句执行成功");}}直接运行这段代码是没有问题的。数组列表可以成功删除元素1并打印相应的句子。但是,我们执行以下任何操作:用list.add("3");替换list.remove(item)怎么样?如果你添加list.add("3");第6行,那么代码会不会有错误?如果把if语句中的“1”换成“2”,你是不是很惊讶?如果能正确执行,当然不用问了,所以三个都会报ConcurrentModificationException;执行结果异常出现这些情况的原因简单解释一下:第一,这涉及到多线程操作。迭代器不支持多线程操作。List类内部会维护一个modCount变量,用来记录修改的次数。示例:ArrayList源代码protectedtransientintmodCount=0;每次生成Iterator时,Iterator都会记录modCount,每次调用next()方法时,都会将记录与外部类List的modCount进行比较,如果发现不相等,则多-thread会抛出Edit异常。你为什么这么做?我的理解是你创建了一个和要遍历的集合内容紧耦合的迭代器,也就是说这个迭代器对应的集合内容就是当前的内容。我绝对不希望我在冒泡排序的时候,还有线程往我的集合里插入数据,对吧?所以Java利用这种简单的处理机制,禁止在遍历时修改集合。至于为什么可以删除“1”,原因在于foreach和iterators的hasNext()方法。foreach的语法其实是while(itr.hasNext()){itr.next()},所以每次循环都会先执行hasNext(),然后看看ArrayList的hasNext()是怎么写的:publicbooleanhasNext(){returncursor!=size;}cursor是一个变量,用来标记迭代器的位置,从0开始,每次调用next时执行+1操作,所以:所以代码删除“1”后,size=1,cursor=1,此时hasNext()返回false,结束循环,所以你的迭代器不会调用next去寻找第二个元素,没办法检测modCount,所以不会出现多线程修改异常;但是当你删除“2”时,迭代器又调用了两次next,此时size=1,cursor=2,hasNext()返回true,于是迭代器又傻傻地调用了next(),这也导致了modCount不相等,并抛出多线程修改异常。当你的集合有三个元素时,你会神奇地发现,删除“1”会抛出异常,但是删除“2”是没问题的。原因是和上面程序的执行顺序是一致的。因此,在《阿里巴巴Java开发手册中有这样一条规定》:3.2中,如果转换需要遍历列表或数组,并替换其部分或全部元素值,则需要使用列表迭代器或数组索引来替换元素的值。因为for-each是一站式的,中间没有停止和显示位置信息;所以它不能用来替换元素。3.3.并行迭代如果需要并行遍历多个集合,则需要显式控制迭代器或索引变量,使所有迭代器或索引变量同步前进(就像上面讲Iterator迭代器时所说的组合归约的情况下),只出现一一对应的下标组合)。4.总结for-each循环不仅适用于遍历集合和数组,还可以让你遍历任何实现了Iterator接口的对象;最重要的是它没有性能损失。要修改数组或集合(添加和删除操作),您必须使用for循环。所以在循环遍历所有数据的时候,能用的时候就选它,嘻嘻。
for循环用了那么多次,但是你真的了解吗?相关文章