JAVA中的for-each循环与迭代

在学习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 ConcurrentModificationException
17         }
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 or
12         // compile time error
13         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         @Override
44         public Iterator<T> iterator() {
45             // TODO Auto-generated method stub
46             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         // example1
10         // Iterator<String> iterator = list.iterator();
11         // while (iterator.hasNext()) {
12         // String lang = iterator.next();
13         // list.remove(lang);
14         // }
15
16         // example 2
17         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接口
时间: 2024-10-07 07:36:17

JAVA中的for-each循环与迭代的相关文章

Java中的增强 for 循环 foreach

foreach 是 Java 中的一种语法糖,几乎每一种语言都有一些这样的语法糖来方便程序员进行开发,编译期间以特定的字节码或特定的方式来对这些语法进行处理.能够提高性能,并减少代码出错的几率.在 Java 中还有比如 泛型.自动拆箱.自动装箱.内部类.枚举等等. foreach 是用来对数组或者集合进行遍历的语法.具体语法如下: 1 2 3 for(元素类型 ele : 数组名/Iterable 实例){   } 下面我们用 foreach 来对数组和一个集合进行遍历: 1 2 3 4 5 6

Java中如何跳出多重循环?

在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出外层循环.例如, ok: for(int i=0;i<10;i++) { for(int j=0;j<10;j++) { System.out.println(“i=” + i + “,j=” + j); if(j == 5) break ok; } } 另外,不建议使用标号这种方式,而是让外层的循环条件表达式的结果可以受到里层循环体代码的控制,例如,要在二维数组中

java中的for each循环

1.for each语句是java 5的新特征之一,在遍历集合,数组方面提供了很大的便利. 2.for each语句是for语句的特殊简化版本,但是for each语句并不能完全取代for语句,然而,任何的for each语句都可以改写为for语句版本. 3.for each的语句格式: for(元素数据类型  元素变量:遍历对象) { //循环体内容 } import java.util.Arrays; import java.util.List; import java.util.Array

052、Java中使用do…while循环实现1~100的累加

01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public static void main(String[] args) { int sum = 0; // 保存总和 int current = 1; // 循环的初始化条件 do { // 循环结束条件 sum += current; // 累加 current++; // 改变循环条件 } while

Java中迭代列表中数据时几种循环写法的效率比较

Java中经常会用到迭代列表数据的情况,本文针对几种常用的写法进行效率比较.虽然网上已经有了类似的文章,但是对他们的结论并不认同. 常见的实现方法: 1.for循环: [java] view plaincopyprint? for(int i = 0; i < list.size(); i++) for(int i = 0, size = list.size(); i < size; i++) 一般人都会认为第二种写法效率高. 2.foreach: [java] view plaincopyp

Java 中break和continue结合标签标示符中断循环示例详解(附源码)

臭名昭著的goto 编程语言中一开始就有goto关键词了.事实上,goto起源于汇编语言的程序控制:"若条件A成立,则跳到这里:否则跳到那里".如果阅读由编译器最终生成的汇编代码,就会发现程序控制里包含了许多跳转.(Java编译器生成它自己的"汇编代码",但是这个代码是运行在Java虚拟机上的,而不是直接运行在CPU硬件上.) goto语句是在源码级上的跳转,这使其招致了不好的声誉.若一个程序总是从一个地方跳到另一个地方,还有什么办法能识别程序的控制流程呢?自从Ed

细说java中Map的两种迭代方式

以前对java中迭代方式总是迷迷糊糊的,今天总算弄懂了,特意的总结了一下,基本是算是理解透彻了. 1.再说Map之前先说下Iterator: Iterator主要用于遍历(即迭代访问)Collection集合中的元素,Iterator也称为迭代器.它仅仅只有三个方法:hasNext(),next()和remove() hasNext():如果仍有元素可以迭代,则返回 true.(换句话说,如果 next 返回了元素而不是 抛出异常,则返回 true). next():返回迭代的下一个元素. re

&#8203;Java中使用for循环对多个Jbutton按钮监听

Java中Jbutton按钮监听,当多个按钮做的事差不多的时候是可以用for循环的(比如计算器1-9)省事省力. 监听时变量只能是final或者static,不过这些在for循环中明显不行,所以,就在循环里加了个final变量. 嘿嘿,轻松搞定. package com.xinbo; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.

Java中break和continue跳出指定循环

在Java中,break和continue可以跳出指定循环,在循环前指定一个标签名称,使用break跳出即可.continue同. 代码如下: (1) 简单的一个continue会退回最内层循环的开头(顶部),并继续执行.        (2) 带有标签的continue会到达标签的位置,并重新进入紧接在那个标签后面的循环 (3) break会中断当前循环,并移离当前标签的末尾. (4) 带标签的break会中断当前循环,并移离由那个标签指示的循环的末尾. 扩展: Java中,goto语句作为保