JAVA中的for-each循环与迭代

来源:互联网 发布:上海大学图书馆数据库 编辑:程序博客网 时间:2024/05/20 16:44

JAVA中的for-each循环与迭代

转载至:http://www.cnblogs.com/gl-developer/p/5971372.html

在学习java中的collection时注意到,collection层次的根接口Collection实现了Iterable<T>接口(位于java.lang包中),实现这个接口允许对象成为 "foreach" 语句的目标,而此接口中的唯一方法,实现的就是返回一个在一组 T 类型的元素上进行迭代的迭代器。

一、迭代器Iterator

接口:Iterator<T>

复制代码
1  public interface Iterator<E>{2 3  boolean hasNext();4 5  E next();6 7  void remove();8  }
复制代码

查看Iterator接口API可以知道,这是对collection进行迭代的迭代器。迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素。 

尤其值得注意的是此迭代器remove()方法的使用:从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。每次调用 next 只能调用一次此方法。如果进行迭代时用调用此方法(remove方法)之外的其他方式修改了该迭代器所指向的 collection,则迭代器的行为是不确定的。接口设计人员在设计Iterator<T>接口的时候已经指出,在进行迭代时如果调用了除了迭代器的remove()方法修改了该迭代器所指向的collection,则会造成不确定的后果。具体出现什么后果依迭代器的具体实现而定。针对这种不确定的后果可能出现的情况,在学习ArrayList时遇到了其中一种:迭代器抛出ConcurrentModificationException异常。具体异常情况如下代码所示:

复制代码
 1 import java.util.ArrayList; 2 import java.util.Collection; 3 import java.util.Iterator; 4  5 public class ItaratorTest { 6  7     public static void main(String[] args) { 8         Collection<String> list = new ArrayList<String>(); 9         list.add("Android");10         list.add("IOS");11         list.add("Windows Mobile");12 13         Iterator<String> iterator = list.iterator();14         while (iterator.hasNext()) {15             String lang = iterator.next();16             list.remove(lang);//will throw ConcurrentModificationException17         }18     }19 20 }
复制代码

此段代码在运行时会抛出ConcurrentModificationException异常,因为我们在迭代器运行期间没有用iterator的remove()方法来删除元素,而是使用ArrayList的 remove()方法改变了迭代器所指向的collection。这就违反了迭代器的设计原则,所以发生了异常。
所报异常情况如下所示:

Exception in thread "main" java.util.ConcurrentModificationException    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)    at java.util.ArrayList$Itr.next(ArrayList.java:831)    at Text.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循环也有两点需要注意。

  1. 使用foreach循环的对象,必须实现了Iterable<T>接口

请看如下示例:

复制代码
 1 import java.util.ArrayList; 2  3 public class ForeachTest1 { 4  5     public static void main(String args[]) { 6         CustomCollection<String> myCollection = new CustomCollection<String>(); 7         myCollection.add("Java"); 8         myCollection.add("Scala"); 9         myCollection.add("Groovy");10 11         // What does this code will do, print language, throw exception or12         // compile time error13         for (String language : myCollection) {14             System.out.println(language);15         }16     }17 18     private class CustomCollection<T> {19         private ArrayList<T> bucket;20 21         public CustomCollection() {22             bucket = new ArrayList();23         }24 25         public int size() {26             return bucket.size();27         }28 29         public boolean isEmpty() {30             return bucket.isEmpty();31         }32 33         public boolean contains(T o) {34             return bucket.contains(o);35         }36 37         public boolean add(T e) {38             return bucket.add(e);39         }40 41         public boolean remove(T o) {42             return bucket.remove(o);43         }44 45     }46 }
复制代码

 

 

上述代码将无法通过编译,这是因为代码中的CustomCollection类没有实现Iterable<T>接口,编译期的报错如下:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:     Can only iterate over an array or an instance of java.lang.Iterable    at Text.ForeachTest1.main(ForeachTest1.java:15)

事实上,无需等到编译时才发现报错,eclipse会在这段代码写完之后就会在foreach循环处显示错误:Can only iterate over an array or an instance of java.lang.Iterable

从上述示例可以再次得到确认的是,foreach循环只适用于实现了Iterable<T>接口的对象。由于所有内置Collection类都实现了java.util.Collection接口,已经继承了Iterable,所以为了解决上述问题,可以选择简单地让CustomCollection实现Collection接口或者继承AbstractCollection。解决方式如下:

复制代码
 1 import java.util.AbstractCollection; 2 import java.util.ArrayList; 3 import java.util.Iterator; 4  5 public class ForeachTest { 6     public static void main(String args[]) { 7         CustomCollection<String> myCollection = new CustomCollection<String>(); 8         myCollection.add("Java"); 9         myCollection.add("Scala");10         myCollection.add("Groovy");11         for (String language : myCollection) {12             System.out.println(language);13         }14     }15 16     private static class CustomCollection<T> extends AbstractCollection<T> {17         private ArrayList<T> bucket;18 19         public CustomCollection() {20             bucket = new ArrayList();21         }22 23         public int size() {24             return bucket.size();25         }26 27         public boolean isEmpty() {28             return bucket.isEmpty();29         }30 31         public boolean contains(Object o) {32             return bucket.contains(o);33         }34 35         public boolean add(T e) {36             return bucket.add(e);37         }38 39         public boolean remove(Object o) {40             return bucket.remove(o);41         }42 43         @Override44         public Iterator<T> iterator() {45             // TODO Auto-generated method stub46             return bucket.iterator();47         }48     }49 }
复制代码

 

 

  2.foreach循环的内部实现也是依靠Iterator进行实现的

为了验证foreach循环是使用Iterator作为内部实现这一事实,我们依然采用本文最开始的实例进行验证:

复制代码
 1 public class ItaratorTest { 2  3     public static void main(String[] args) { 4         Collection<String> list = new ArrayList<String>(); 5         list.add("Android"); 6         list.add("IOS"); 7         list.add("Windows Mobile"); 8  9         // example110         // Iterator<String> iterator = list.iterator();11         // while (iterator.hasNext()) {12         // String lang = iterator.next();13         // list.remove(lang);14         // }15 16         // example 217         for (String language : list) {18             list.remove(language);19         }20     }21 22 }
复制代码

程序运行时所报异常:

Exception in thread "main" java.util.ConcurrentModificationException    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)    at java.util.ArrayList$Itr.next(ArrayList.java:831)    at Text.ItaratorTest.main(ItaratorTest.java:22)

此异常正说明了for-each循环内部使用了Iterator来遍历Collection,它也调用了Iterator.next(),这会检查(元素的)变化并抛出ConcurrentModificationException。

总结:

  • 在遍历collection时,如果要在遍历期间修改collection,则必须通过Iterator/listIterator来实现,否则可能会发生“不确定的后果”。
  • foreach循环通过iterator实现,使用foreach循环的对象必须实现Iterable接口
  • foreach这种循环一般只适合做数组的遍历,提取数据显示等,不适合用于增加删除和使用下标等复杂的操作.但它不能像Iterator一样可以人为的控制,而且也不能调用iterator.remove();更不能使用下标来方便的访问元素。
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 打发过了的奶油怎么办? 孩子生了没人带怎么办 办理退休审核未通过怎么办 整天沉迷全民k歌怎么办 吃鸡账号被误封了怎么办 手机换号了银行卡怎么办 评副高没有病房论文怎么办 收件人姓名写错了怎么办 汇款姓名写错了怎么办 在菲律宾旅游签证过期怎么办 微博转发被限制怎么办 百香果蜂蜜沉在下面怎么办 领导要潜我老婆怎么办 被领导潜了该怎么办 实习期单位领导开除我我该怎么办 泡泡袖显手臂肥怎么办 客人想吃霸王餐怎么办 在四楼上课时发生地震怎么办 学生在上课时发生地震怎么办 学生上课时发生地震怎么办 老板承诺的工资不兑现怎么办 只有本科毕业证没有学位证怎么办 孩子跟坏孩子玩怎么办 苕帚跟簸箕掉下来了怎么办 身体发烫又觉得冷怎么办 买苹果8后悔了怎么办 手机死机了怎么办不可拆卸电池 百度网盘密码忘了怎么办 空调冷凝水无法排出怎么办 转账时名字错了怎么办 打款名字错了怎么办 转账名字写错了怎么办 国际汇款汇错了怎么办 汇款英文写错了怎么办 体重秤不显示了怎么办 在ur试完衣服怎么办 汽车主机没有倒车检测线怎么办 合格考补考没过怎么办 合同一式两份双方都丢失怎么办 自控力差的人怎么办 孩子缺乏自控能力该怎么办