Java for-each循环解惑

2014/04/24 | 分类: 技术之外 | 0 条评论 | 标签: JAVA

分享到:21
本文由 ImportNew - liqing 翻译自 javarevisited。欢迎加入翻译小组。转载请见文末要求。

从Java5起,在Java中有了for-each循环,可以用来循环遍历collection和array。For each循环允许你在无需保持传统for循环中的索引,或在使用iterator /ListIterator时无需调用while循环中的hasNext()方法就能遍历collection。Java中,for-each循环简化了任何Collection或array的遍历过程,但并不是每个Java程序员都了解本文将要描述的for-each 循环的一些细节。与 Java5 发布的其他术语:释放别名泛型,自动封装和可变参数不同,Java开发者对for-each循环的使用比任何其他特性更加频繁,但当问及高级的for-each循环怎样工作,或什么是在for-each循环中使用Collection时的基本需求时,就不是每个人都能够回答的了。本篇教程和例子旨在通过深入研究for-each 循环中几个有趣的难题来填补上述空白(说明上述问题)。好了,不再赘述,一起看看我们在Java5 for-each循环的第一个问题。

高级循环问题 1

考虑下面这段遍历一个用户自定义的aggregator或collection类的代码,这段代码将会打印出什么,抛出异常还是编译器错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package test;
 
/**
  * Java Class to show how for-each loop works in Java
  */
public class ForEachTest { 
 
    public static void main(String args[]){
        CustomCollection<String> myCollection = new CustomCollection<String>();
        myCollection.add("Java");
        myCollection.add("Scala");
        myCollection.add("Groovy");
 
        //What does this code will do, print language, throw exception or compile time error
        for(String language: myCollection){
            System.out.println(language);
        }
    }
}

下面是我们的CustomCollection类,这是个参数为泛型的类,与任何其他的Collection类相似,依靠于ArrayList并提供从Collection中添加和删除项的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package test;
 
public class CustomCollection<T>{
    private ArrayList<T> bucket;
 
    public CustomCollection(){
        bucket = new ArrayList();
    }
 
    public int size() {
        return bucket.size();
    }
 
    public boolean isEmpty() {
        return bucket.isEmpty();
    }
 
    public boolean contains(T o) {
        return bucket.contains(o);
    }
 
    public boolean add(T e) {
        return bucket.add(e);
    }
 
    public boolean remove(T o) {
        return bucket.remove(o);
    }  
 
}

答案:

上述代码将无法通过编译,这是因为我们的CustomCollection类没有实现java.lang.Iterable接口,编译期错误如下:

1
2
3
4
5
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - for-each not applicable to expression type
 
 required: array or java.lang.Iterable
  found:    test.CustomCollection
        at test.ForEachTest.main(ForEachTest.java:24)

从中了解到的一个有趣的事实是:for-each循环仅应用于实现了Iterable接口的Java array和Collection类,而且既然所有内置Collection类都实现了java.util.Collection接口,已经继承了Iterable,这一细节通常会被忽略,这点可以在Collection接口的类型声明“ public interface Collection extends Iterable”中看到。所以为了解决上述问题,你可以选择简单地让CustomCollection实现Collection接口或者继承AbstractCollection,这是默认的通用实现并展示了如何同时使用抽象类和接口以获取更好的灵活性。现在让我们来看看for-each循环的第二个难题:

Java for-each循环的第二个难题:

在下面的代码示例将会抛出ConcurrentModificationException异常。这里我们使用标准iterator和for-each循环遍历ArrayList,随后删除元素,你需要找出哪段代码将会抛出ConcurrentModificationException ,为什么?请注意,答案可能是两个都会,都不会或其中之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package test;
 
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
 
/**
  * Java class to demonstrate inner working of for-each loop in Java
  * @author Javin Paul
  **/
public class ForEachTest2 { 
 
    public static void main(String args[]){
        Collection<String> list = new ArrayList<String>();
        list.add("Android");
        list.add("iPhone");
        list.add("Windows Mobile");
 
        // Which Code will throw ConcurrentModificationException, both,
       // none or one of them
 
        // example 1       
        Iterator<String> itr = list.iterator();
        while(itr.hasNext()){
            String lang = itr.next();
            list.remove(lang);
        }
 
         // example 2
        for(String language: list){
            list.remove(language);
        }
    }
}

大约70%的Java开发者都会说第一个代码块会抛出ConcurrentModificationException异常,因为我们没有用iterator的remove方法来删除元素,而是使用ArrayList的 remove()方法。但是,没有多少Java开发者会说出for-each循环也会出现同样的问题,因为我们在这里没有使用iterator。事实上,第二个代码片段也会抛出ConcurrentModificationException异常,这点在解决了第一个困惑之后就变得很明显了。既然for-each循环内部使用了Iterator来遍历Collection,它也调用了Iterator.next(),这会检查(元素的)变化并抛出ConcurrentModificationException。你可以从下面的输出中了解到这点,在注释掉第一个代码段后,当你运行第二个代码段时会得到下面的输出。

1
2
3
4
Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
        at java.util.AbstractList$Itr.next(AbstractList.java:343)
        at test.ForEachTest2.main(ForEachTest2.java:34)

以上就是关于Java5 for-each循环的全部内容。我们已经看到了Java程序员在编写遍历Collection类的代码时产生的很多问题,特别是在遍历collection的同时删除元素的时候。请牢记,在从任何Collection(例如Map、Set或List)中删除对象时总要使用Iterator的remove方法,也请谨记for-each循环只是标准Iterator代码标准用法之上的一种语法糖(syntactic sugar)而已。

译者注:语法糖(syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达-Peter J. Landin发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

原文链接: javarevisited 翻译: ImportNew.comliqing
译文链接: http://www.importnew.com/11038.html
转载请保留原文出处、译者和译文链接。]

Java for-each循环解惑的更多相关文章

  1. java基础3_循环语句,数组

    java中的循环: Java中提供了3中循环结构:  while  do-while  for ① 循环结构的作用? 可以不断重复执行循环结构中的代码: ② 上面的3个循环结构功能都是一样的,只是结构 ...

  2. java跳出外部循环

    java跳出外部循环两种方法 //用flag标识 boolean flag = true; for(int i=0;i<10&&flag;i++){ for(int j=0;j& ...

  3. java中for循环的6种写法

    有些写法上的说明写的过于武断,可能有很多不当之处,仅供参考.   package ForLoop; import java.util.ArrayList; import java.util.Itera ...

  4. java关于for循环。

    众所周知,JAVA中for循环的基本格式为: for(初始化表达式:布尔表达式:循环后更新表达式){循环体} 举个例子来说可以写成 (1)for (int x=1;x<10;x++){ Syst ...

  5. Java中for_each循环的使用

    最近在看一些和安卓相关的书籍,看到了for_each这种循环结构,这是为了简化java的for循环而改造的一种方便使用的格式. 格式如下: for(数据类型 变量:集合) 语句块 接下来看一个例程: ...

  6. java中的循环方法(附带本人遇到的坑)

    java循环结构 顺序结构的程序语句只能 被执行一次.如果你要同样的操作执行多次,就需要使用循环结构. java中有三种主要的循环结构: 1.while 循环 2.do...while 循环 3.fo ...

  7. Java基础之循环语句、条件语句、switch case 语句

    Java 循环结构 - for, while 及 do...while 顺序结构的程序语句只能被执行一次.如果您想要同样的操作执行多次,,就需要使用循环结构. Java中有三种主要的循环结构: whi ...

  8. Java 增强 for 循环

    Java 增强 for 循环 Java5 引入了一种主要用于数组的增强型 for 循环. Java 增强 for 循环语法格式如下: for(声明语句 : 表达式) { //代码句子 } 声明语句:声 ...

  9. java用while循环设计轮询线程的性能问题

    java用while循环设计轮询线程的性能问题 轮询线程在开发过程中的应用是比较广泛的,在这我模拟一个场景,有一个队列和轮询线程,主线程往队列中入队消息,轮询线程循环从队列中读取消息并打印消息内容.有 ...

随机推荐

  1. python学习------模块

    模块(modue)的概念 在计算机程序开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件 ...

  2. tensorflow estimator API小栗子

    TensorFlow的高级机器学习API(tf.estimator)可以轻松配置,训练和评估各种机器学习模型. 在本教程中,您将使用tf.estimator构建一个神经网络分类器,并在Iris数据集上 ...

  3. fwrite文件写入数据

    文件的操作就两种:读和写 读:把文件中的内容读入到程序中,然后根据自己的项目需求把文件的数据进行相关的处理. 写:就是将程序中的数据,写入到文件中,去更新文件. 这么两种操作归到代码中就是这两种函数: ...

  4. 13. Roman to Integer ★

    题目内容: Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range fr ...

  5. python enumerate用法总结

    enumerate()说明enumerate()是python的内置函数enumerate在字典上是枚举.列举的意思对于一个可迭代的(iterable)/可遍历的对象(如列表.字符串),enumera ...

  6. mybatis中<foreach>标签的使用

    标签太多,记一下不是特别常用的标签~留着脑袋瓜不机灵的时候看! <foreach>标签  该标签的作用是遍历集合类型的条件 <select id="countByUserL ...

  7. 使用LinkedList类生成一个集合对象,循环加入“小样1”,“小样2”,“小样3”,“小样4”,“小样5”……“小样100”。输出这个集合的大小。再使用循环删除这个集合中所有名字为偶数的对象,比如“小样6”,“小样100”,都是偶数名。最后:循环输出集合中所有的对象,看是否删除成功。

    package com.lanxi.demo1_8; import java.util.Iterator; import java.util.LinkedList; public class Test ...

  8. 可编程逻辑控制器(PLC)漏洞挖掘思路与验证

    mailto wangkai0351@gmail.com 随时记录千奇百怪的漏洞挖掘思路,主要针对STEP7 v5+西门子S7-300/400系列PLC,欢迎同行前来交流. 组态信息下载完整性攻击 思 ...

  9. io 的一些简单说明及使用

    io 流简述: i->inputStream(输入流) o->outputStream  (输出流) IO流简单来说就是Input和Output流,IO流主要是用来处理设备之间的数据传输, ...

  10. 马凯军201771010116《面向对象与程序设计Java》第十六周知识学习总结

    一:理论知识部分 1.线程的概念: 程序是一段静态的代码,它是应用程序执行的蓝 本. ‐进程是程序的一次动态执行,它对应了从代码加 载.执行至执行完毕的一个完整过程. 多线程是进程执行过程中产生的多条 ...