“Foreach(增强for)”的版本间差异

来自Wikioe
跳到导航 跳到搜索
无编辑摘要
第4行: 第4行:
  For-Each 循环也叫增强型的 for 循环,或者叫 foreach 循环,是 JDK5.0 的新特性(其他新特性比如泛型、自动装箱等),For-Each 循环的加入简化了集合的遍历。
  For-Each 循环也叫增强型的 for 循环,或者叫 foreach 循环,是 JDK5.0 的新特性(其他新特性比如泛型、自动装箱等),For-Each 循环的加入简化了集合的遍历。
   
   
  这种方式并不是新的语法,只是<big>'''语法糖'''</big>。即编写的 foreach 循环的代码并不是直接转成字节码,而是由编译器先转成对应的语法,然后再转成字节码,可以理解成是编译器对一些语法的封装提供的另一种方便阅读编写功能代码的实现方式。
  这种方式并不是新的语法,只是<span style="color: green"><big>'''语法糖'''</big></span>。即编写的 foreach 循环的代码并不是直接转成字节码,而是由编译器先转成对应的语法,然后再转成字节码,可以理解成是编译器对一些语法的封装提供的另一种方便阅读编写功能代码的实现方式。
Java 中提供的 foreach 语法糖其底层实现方式主要有两种:
1、对于集合类或实现迭代器的集合使用'''迭代器'''的遍历方式;
2、对于数组集合使用'''数组'''的遍历方法。
 
* '''foreach 可以与任何实现了 Iterable 接口的对象一起工作'''


=== 语法 ===
=== 语法 ===
第54行: 第48行:


== 原理 ==
== 原理 ==
'''foreach 可以与任何实现了 <span style="color: green">Iterable</span> 接口的对象一起工作。'''
(Iterable 接口提供了一个 Iterator 迭代器)
Java 中提供的 foreach 语法糖其底层实现方式主要有两种:
# '''迭代器遍历模式''':对于集合类(或实现迭代器的集合),使用“迭代器”的遍历方式;
#: 以下两组代码等效:
#: <syntaxhighlight lang="java" highlight="">
import java.util.ArrayList;
import java.util.List;
public class TestForEach
{
    public static void main(String args[])
    {
        List<Integer> nums = new ArrayList<>();
        nums.add(11);
        nums.add(22);
        nums.add(33);
        for (Integer num : nums)
        {
            System.out.println(num);
        }
    }
}
</syntaxhighlight>
#: <syntaxhighlight lang="java" highlight="">
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class TestForEach
{
    public static void main(String args[])
    {
        List<Integer> nums = new ArrayList<>();
        nums.add(11);
        nums.add(22);
        nums.add(33);
        // 此处没有使用泛型,因为泛型在 java 中也是一种语法糖,只是编译器提供的一种检查,在运行期会擦除类型信息,其并不像C++那样在语法层面真正的支持泛型
        // 当然,为了良好的编码习惯,在平时的编码中应该使用泛型,即 Iterator<Integer> iter = nums.iterator();
        Iterator iter = nums.iterator();
        while (iter.hasNext())
        {
            Integer num = (Integer)iter.next();
            System.out.println(num);   
        }
    }
}
</syntaxhighlight>
# '''数组依次遍历模式''':对于数组(没有实现 Iterator 接口)使用最基本的依次遍历数组中每个元素的方式,来实现遍历。
#: 以下两组代码等效:
#: <syntaxhighlight lang="java" highlight="">
public class TestForEach
{
    public static void main(String args[])
    {
        int[] nums = {11, 22, 33};
        for (int num : nums)
        {
            System.out.println(num);
        }
    }
}
</syntaxhighlight>
#: <syntaxhighlight lang="java" highlight="">
public class TestForEach
{
    public static void main(String args[])
    {
        int[] nums = {11, 22, 33};
        for (int i = 0; i < nums.length; i++)
        {
            int num = nums[i];
            System.out.println(num);
        }
    }
}
</syntaxhighlight>
== '''<span style="color: green">为什么不能在 foreach 中增加、删除集合元素?</span>''' ==
使用 foreach 进行集合遍历时需要额外注意不能对集合长度进行修改,也就是不能对集合进行增删操作,否则会抛出 <big>'''ConcurrentModificationException'''</big> 异常。
错误的增删方式:
: <syntaxhighlight lang="java" highlight="">
import java.util.ArrayList;
import java.util.List;
public class TestForEach
{
    public static void main(String args[])
    {
        List<Integer> nums = new ArrayList<>();
        nums.add(11);
        nums.add(22);
        nums.add(33);
        for (Integer num : nums)
        {
            if (num == 11)
            {
                // 此处使用集合中的 remove 操作,而不是迭代器中的 remove 操作,
                // 会导致迭代器中的 expectedModCount 和集合中的 modCount 变量不相等,从而导致在执行 next() 函数时抛出异常
                nums.remove((Integer)num);
            }
            else
            {
                System.out.println(num);
            }
        }
    }
}
</syntaxhighlight>
正确的增删方式:【使用 Iterator 的增删方法】
: <syntaxhighlight lang="java" highlight="">
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class TestForEach
{
    public static void main(String args[])
    {
        List<Integer> nums = new ArrayList<>();
        nums.add(11);
        nums.add(22);
        nums.add(33);
        Iterator<Integer> iter = nums.iterator();
        while (iter.hasNext())
        {
            Integer num = iter.next();
            if (num == 11)
            {
                // 在迭代器遍历中,不能使用集合自有的删除操作,只能使用迭代器中的删除操作,
                // 否则会导致迭代器中的 expectedModCount 和集合中的 modCount 变量不相等,从而导致在执行 next() 函数时抛出异常
                //nums.remove((Integer)num);
                iter.remove();
            }
            else
            {
                System.out.println(num); 
            }
        }     
    }
}
</syntaxhighlight>


== for、foreach、Iterator ==
=== 迭代器的“expectedModCount” 与 集合的“modCount” ===






=== 性能比较 ===
== 性能 ==
由于 foreach 作为语法糖,在不同的场景实现方式不同,所以其性能分别近似于 for(对于数组) 和 Iterator(对于集合)。
 
由下代码可以看出:
由下代码可以看出:
# ArrayList、LinkList 中:Iterator > foreach > for;
# 对于集合(ArrayList、LinkList),性能表现:Iterator >= foreach > for;
# 不要在 LinkList 中使用 for,效率太低;
#* 不要在 LinkList 中使用 for,效率太低;
# 对于数组,性能表现:foreach >= for;
#*(数组不支持 Iterator)


<syntaxhighlight lang="java" highlight="">
<syntaxhighlight lang="java" highlight="">
第74行: 第221行:
     ArrayList<Integer> arrayList = new ArrayList<>();
     ArrayList<Integer> arrayList = new ArrayList<>();
     LinkedList<Integer> linkedList = new LinkedList<>();
     LinkedList<Integer> linkedList = new LinkedList<>();
    String[] strings = new String[100000000];


     public TestFor() {
     public TestFor() {
第79行: 第228行:
             arrayList.add(i);
             arrayList.add(i);
             linkedList.add(i);
             linkedList.add(i);
        }
        for (int i = 0; i < 100000; i++) {
            strings[i] = "eijux" + i;
         }
         }
     }
     }
第85行: 第238行:
         int number;
         int number;
         long start, end, result;
         long start, end, result;
        System.out.println("========== ArrayList ==========");


         start = System.currentTimeMillis();
         start = System.currentTimeMillis();
第105行: 第260行:
         start = System.currentTimeMillis();
         start = System.currentTimeMillis();
         Iterator<Integer> itr = arrayList.iterator();
         Iterator<Integer> itr = arrayList.iterator();
         while (itr.hasNext()){
         while (itr.hasNext()) {
             number = itr.next();
             number = itr.next();
         }
         }
第116行: 第271行:
         int number;
         int number;
         long start, end, result;
         long start, end, result;
        System.out.println("========== LinkList ==========");


         start = System.currentTimeMillis();
         start = System.currentTimeMillis();
第135行: 第292行:
         start = System.currentTimeMillis();
         start = System.currentTimeMillis();
         Iterator<Integer> itr = linkedList.iterator();
         Iterator<Integer> itr = linkedList.iterator();
         while (itr.hasNext()){
         while (itr.hasNext()) {
             number = itr.next();
             number = itr.next();
         }
         }
第141行: 第298行:
         result = end - start;
         result = end - start;
         System.out.println("LinkList Iterator循环消耗时间:" + result);
         System.out.println("LinkList Iterator循环消耗时间:" + result);
    }
    public void testArray() {
        String string;
        long start, end, result;
        System.out.println("========== Array ==========");
        start = System.currentTimeMillis();
        for (int i = 0; i < strings.length; i++) {
            string = strings[i];
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("String[] for循环消耗时间:" + result);
        start = System.currentTimeMillis();
        for (String str : strings) {
            string = str;
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("String[] foreach循环消耗时间:" + result);
     }
     }


第146行: 第326行:
         (new TestFor()).testArrayList();
         (new TestFor()).testArrayList();
         (new TestFor()).testLinkList();
         (new TestFor()).testLinkList();
        (new TestFor()).testArray();
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>
结果:
结果:
: <syntaxhighlight lang="java" highlight="">
: <syntaxhighlight lang="java" highlight="">
ArrayList for循环消耗时间:24
========== ArrayList ==========
ArrayList foreach循环消耗时间:11
ArrayList for循环消耗时间:42
ArrayList Iterator循环消耗时间:6
ArrayList foreach循环消耗时间:9
LinkList for循环消耗时间:25181
ArrayList Iterator循环消耗时间:7
LinkList foreach循环消耗时间:19
========== LinkList ==========
LinkList Iterator循环消耗时间:8
LinkList for循环消耗时间:22322
LinkList foreach循环消耗时间:8
LinkList Iterator循环消耗时间:5
========== Array ==========
String[] for循环消耗时间:21
String[] foreach循环消耗时间:11
</syntaxhighlight>
</syntaxhighlight>



2022年4月23日 (六) 19:22的版本


关于

For-Each 循环也叫增强型的 for 循环,或者叫 foreach 循环,是 JDK5.0 的新特性(其他新特性比如泛型、自动装箱等),For-Each 循环的加入简化了集合的遍历。

这种方式并不是新的语法,只是语法糖。即编写的 foreach 循环的代码并不是直接转成字节码,而是由编译器先转成对应的语法,然后再转成字节码,可以理解成是编译器对一些语法的封装提供的另一种方便阅读编写功能代码的实现方式。

语法

for(type element: array)
{
   System.out.println(element);
}

使用

public class ForeachTest
{
    public static void main(String[] args)
    {
        int[] arr = {1, 2, 3, 4, 5};
        
        System.out.println("----------普通for遍历------------");
        for(int i=0; i<arr.length; i++)
        {
            System.out.println(arr[i]);
        }
        
        System.out.println("---------增强for遍历-------------");
        for(int element:arr)
        {
            System.out.println(element);
        }
        
        System.out.println("---------二维数组遍历-------------");
        int[][] arr2 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} ;
        for(int[] row : arr2)
        {
            for(int element : row)
            {
                System.out.println(element);
            }
        }
    }
}

原理

foreach 可以与任何实现了 Iterable 接口的对象一起工作。

(Iterable 接口提供了一个 Iterator 迭代器)

Java 中提供的 foreach 语法糖其底层实现方式主要有两种:

  1. 迭代器遍历模式:对于集合类(或实现迭代器的集合),使用“迭代器”的遍历方式;
    以下两组代码等效:
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestForEach
    {
        public static void main(String args[])
        {
            List<Integer> nums = new ArrayList<>();
            nums.add(11);
            nums.add(22);
            nums.add(33);
    
            for (Integer num : nums)
            {
                System.out.println(num);
            }
        }
    }
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Iterator;
    
    public class TestForEach
    {
        public static void main(String args[])
        {
            List<Integer> nums = new ArrayList<>();
            nums.add(11);
            nums.add(22);
            nums.add(33);
    
            // 此处没有使用泛型,因为泛型在 java 中也是一种语法糖,只是编译器提供的一种检查,在运行期会擦除类型信息,其并不像C++那样在语法层面真正的支持泛型
            // 当然,为了良好的编码习惯,在平时的编码中应该使用泛型,即 Iterator<Integer> iter = nums.iterator();
            Iterator iter = nums.iterator();
            while (iter.hasNext()) 
            {
                Integer num = (Integer)iter.next();
                System.out.println(num);    
            }
        }
    }
    
  2. 数组依次遍历模式:对于数组(没有实现 Iterator 接口)使用最基本的依次遍历数组中每个元素的方式,来实现遍历。
    以下两组代码等效:
    public class TestForEach
    {
        public static void main(String args[])
        {
            int[] nums = {11, 22, 33};
            for (int num : nums)
            {
                System.out.println(num);
            }
        }
    }
    
    public class TestForEach
    {
        public static void main(String args[])
        {
            int[] nums = {11, 22, 33};
            for (int i = 0; i < nums.length; i++)
            {
                int num = nums[i];
                System.out.println(num);
            }
        }
    }
    

为什么不能在 foreach 中增加、删除集合元素?

使用 foreach 进行集合遍历时需要额外注意不能对集合长度进行修改,也就是不能对集合进行增删操作,否则会抛出 ConcurrentModificationException 异常。

错误的增删方式:

import java.util.ArrayList;
import java.util.List;

public class TestForEach
{
    public static void main(String args[])
    {
        List<Integer> nums = new ArrayList<>();
        nums.add(11);
        nums.add(22);
        nums.add(33);

        for (Integer num : nums)
        {
            if (num == 11)
            {
                // 此处使用集合中的 remove 操作,而不是迭代器中的 remove 操作,
                // 会导致迭代器中的 expectedModCount 和集合中的 modCount 变量不相等,从而导致在执行 next() 函数时抛出异常
                nums.remove((Integer)num);
            }
            else
            {
                System.out.println(num);
            }
        }
    }
}

正确的增删方式:【使用 Iterator 的增删方法】

import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;

public class TestForEach
{
    public static void main(String args[])
    {
        List<Integer> nums = new ArrayList<>();
        nums.add(11);
        nums.add(22);
        nums.add(33);

        Iterator<Integer> iter = nums.iterator();
        while (iter.hasNext()) 
        {
            Integer num = iter.next();
            if (num == 11)
            {
                // 在迭代器遍历中,不能使用集合自有的删除操作,只能使用迭代器中的删除操作,
                // 否则会导致迭代器中的 expectedModCount 和集合中的 modCount 变量不相等,从而导致在执行 next() 函数时抛出异常
                //nums.remove((Integer)num);
                iter.remove();
            }
            else
            {
                System.out.println(num);   
            } 
        }       
    }
}

迭代器的“expectedModCount” 与 集合的“modCount”

性能

由于 foreach 作为语法糖,在不同的场景实现方式不同,所以其性能分别近似于 for(对于数组) 和 Iterator(对于集合)。

由下代码可以看出:

  1. 对于集合(ArrayList、LinkList),性能表现:Iterator >= foreach > for;
    • 不要在 LinkList 中使用 for,效率太低;
  2. 对于数组,性能表现:foreach >= for;
    • (数组不支持 Iterator)
package com.eijux;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

public class TestFor {
    ArrayList<Integer> arrayList = new ArrayList<>();
    LinkedList<Integer> linkedList = new LinkedList<>();

    String[] strings = new String[100000000];

    public TestFor() {
        for (int i = 0; i < 100000; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }

        for (int i = 0; i < 100000; i++) {
            strings[i] = "eijux" + i;
        }
    }

    public void testArrayList() {
        int number;
        long start, end, result;

        System.out.println("========== ArrayList ==========");

        start = System.currentTimeMillis();
        for (int i = 0; i < arrayList.size(); i++) {
            number = arrayList.get(i);
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("ArrayList for循环消耗时间:" + result);


        start = System.currentTimeMillis();
        for (int i : arrayList) {
            number = i;
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("ArrayList foreach循环消耗时间:" + result);

        start = System.currentTimeMillis();
        Iterator<Integer> itr = arrayList.iterator();
        while (itr.hasNext()) {
            number = itr.next();
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("ArrayList Iterator循环消耗时间:" + result);
    }

    public void testLinkList() {
        int number;
        long start, end, result;

        System.out.println("========== LinkList ==========");

        start = System.currentTimeMillis();
        for (int i = 0; i < linkedList.size(); i++) {
            number = linkedList.get(i);
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("LinkList for循环消耗时间:" + result);

        start = System.currentTimeMillis();
        for (int i : linkedList) {
            number = i;
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("LinkList foreach循环消耗时间:" + result);

        start = System.currentTimeMillis();
        Iterator<Integer> itr = linkedList.iterator();
        while (itr.hasNext()) {
            number = itr.next();
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("LinkList Iterator循环消耗时间:" + result);
    }

    public void testArray() {
        String string;
        long start, end, result;

        System.out.println("========== Array ==========");

        start = System.currentTimeMillis();
        for (int i = 0; i < strings.length; i++) {
            string = strings[i];
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("String[] for循环消耗时间:" + result);

        start = System.currentTimeMillis();
        for (String str : strings) {
            string = str;
        }
        end = System.currentTimeMillis();
        result = end - start;
        System.out.println("String[] foreach循环消耗时间:" + result);
    }

    public static void main(String[] args) {
        (new TestFor()).testArrayList();
        (new TestFor()).testLinkList();
        (new TestFor()).testArray();
    }
}

结果:

========== ArrayList ==========
ArrayList for循环消耗时间42
ArrayList foreach循环消耗时间9
ArrayList Iterator循环消耗时间7
========== LinkList ==========
LinkList for循环消耗时间22322
LinkList foreach循环消耗时间8
LinkList Iterator循环消耗时间5
========== Array ==========
String[] for循环消耗时间21
String[] foreach循环消耗时间11

集合的三种遍历方式

集合遍历的三种方式:

  1. 普通for
  2. 增强型for
  3. Iterator
public class ForeachTest
{
    public static void main(String[] args)
    { 
        //以三种方式遍历集合List
        List<String> list = new ArrayList<String>();
        list.add("a");
        list.add("b");
        list.add("c");
        
        System.out.println("----------普通for-----------");
        for(int i = 0; i < list.size(); i++)
        {
            System.out.println(list.get(i));
            
        }
		
        System.out.println("----------增强型for(实现了Iterable)-----------");
        for(String str: list)
        {
            System.out.println(str);
        }
        
        System.out.println("----------Iterator-----------");
        for(Iterator<String> iter = list.iterator(); iter.hasNext();)
        {
            System.out.println(iter.next());
        }
    }
}