1、概述

普通 for 循环在遍历集合时使用下标来定位集合中的元素。foreach 循环是一种更加简介的 for 循环,也称增强 for 循环。
虽然 Iterator 可以用来遍历集合中的元素,但写法上比较繁琐,为了简化书写,从 JDK 5.0 开始提供了 foreach 循环,用于遍历数组或集合中的元素,语法格式如下。

  1. for (容器中的元素类型 临时变量 容器变量){
  2. 执行语句
  3. }

可以看出,与 for 循环相比,foreach 循环不需要获得容器长度,也不需要很根据引索访问容器中的元素,但它会自动遍历容器中的每个元素。
在某些情况下,foreach 是不能完全代替 for 循环的。

2、详解

  1. import java.util.ArrayList;
  2. public class example04 {
  3. public static void main(String[] args) {
  4. ArrayList<String> list = new ArrayList<String>(); //创建 ArrayList 集合
  5. list.add("Jack"); //向集合中添加字符串元素
  6. list.add("Rose");
  7. list.add("Tom");
  8. for (Object object : list) { //使用 foreach 循环遍历集合
  9. System.out.println(object); //取出并打印集合中的元素
  10. }
  11. }
  12. }

从上述代码块看出,foreach 循环在遍历集合时语法非常简洁,没有循环条件,没有迭代语句,所有这些工作都交给虚拟机去执行了。
foreach 循环的次数是由容器中元素的个数决定的,每次循环时,foreach 中都通过变量将当前循环的元素记住,从而将集合中的元素分别打印出来。

3.1 局限性

foreach 循环虽然写起来很简洁,但在使用时也存在一定的局限性。当使用 foreach 循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改。

  1. public class example05 {
  2. public static void main(String[] args) {
  3. String [] strings = {"aaa","bbb","ccc"}; //创建string类型数组
  4. for (String string : strings) { //foreach 循环遍历数组
  5. string = "ddd";
  6. }
  7. System.out.println("foreach循环修改后的数组:"+strings[0] + "," + strings[1] + "," + strings[2]);
  8. for (int i = 0; i < strings.length; i++) { //for 循环遍历数组
  9. strings[i] = "ddd";
  10. }
  11. System.out.println("普通for循环修改后的数组:" + strings[0] + "," + strings[1] + "," + strings[2]);
  12. }
  13. }
  14. console
  15. foreach循环修改后的数组:aaa,bbb,ccc
  16. 普通for循环修改后的数组:ddd,ddd,ddd

从上述代码块中,分别使用了 foreach 循环和普通 for 循环去修改数组中的元素。从运行结果可以看出,foreach 循环并不能修改数组中的元素的值。
其原因是第五行代码中的 string = “ddd” 只是将临时变量 string 指向了一个新的字符串,这和数组中的元素没有一点关系。而在普通 for 循环中,是可以通过引索的方式来引用数组中的元素并将其值进行修改的。

3.2 元素的增添和删除

增添和删减的代码是相同的,这里只给出删除的演示代码。且添加可以使用集合自己的 add()方法,不需要使用增强 for 循环。
在使用 Iterator 迭代器对集合中的元素进行迭代时,如果调用了集合对象的 remove()方法去删除元素之后,继续使用迭代器遍历元素,会出现异常。
接下来通过一个案例来演示这种异常。假设在一个集合中存储了学校所有学生的姓名,由于一个名为 Annie 的学生中途转学,这时就需要在迭代集合时找出该元素并将其删除。如下所示。

  1. import java.util.ArrayList;
  2. import java.util.Iterator;
  3. public class example06 {
  4. public static void main(String[] args) {
  5. ArrayList list = new ArrayList(); //创建 ArrayList 集合
  6. list.add("Jack");
  7. list.add("Annie");
  8. list.add("Rose");
  9. list.add("Tom");
  10. Iterator iterator = list.iterator(); //获得 iterator 对象
  11. while (iterator.hasNext()) { //判断该集合是否有下一个元素
  12. Object object = iterator.next(); //获取该集合中的元素
  13. if ("Annie".equals(object)) { //判断该集合中的元素是否为 Annie
  14. list.remove(object); //删除该集合中的元素
  15. }
  16. }
  17. System.out.println(list); //打印集合
  18. }
  19. }
  20. console
  21. Exception in thread "main" java.util.ConcurrentModificationException
  22. at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
  23. at java.util.ArrayList$Itr.next(Unknown Source)
  24. at example06.example06.main(example06.java:15)

上述代码块在运行时出现了并发修改异常 ConcurrentModificationExceptoin。这个异常时迭代器对象抛出的,出现异常的原因时结合中删除了元素会导致迭代器预期的迭代次数发生改变,导致迭代器的结果不准确。
**

3.2.1、解决方案①

从业务逻辑上只想将姓名为 Annie 的学生删除,至于后面还有多少学生并不需要关心,只需找到学生后跳出循环不再迭代即可,也就是在第十五行下面增加一个 break 语句,代码如下。

  1. if ("Annie".equals(object)) {
  2. list.remove(object);
  3. break;
  4. }

在使用 break 语句跳出循环以后,由于没有继续使用迭代器对集合中的元素进行迭代,因此,集合中删除元素对程序没有任何影响,就不会再出现异常。

3.2.2、解决方案②

如果需要在集合的迭代期间对集合中的元素进行删除,可以使用迭代器本身的删除方法,将代码块第十五行代码替换为 iterator.remove()即可解决这个问题。

  1. if ("Annie".equals(object)) {
  2. iterator.remove();
  3. }

替换代码后再次运行程序,两个方案结果一致。如下所示。
QQ截图20200524171620.png
根据运行结果可以看出,学员 Annie 确实被删除了,并且没有出现异常。因此可以得出解困,调用迭代器对象的 remove()方法删除元素所导致的迭代次数变化,对于迭代器对象本身来讲时可预知的。