Java增强for循环--foreach深入探讨

首先我们来认识Java中的foreach,foreach也被称为增强的for循环

我们来看一下他的基本用法

对于基本数据类型的数组遍历有如下用法:

        int[] arr = new int[100];
        // foreach写法
        for (int i : arr) {
            System.out.println(i);
        }

可以看出,对于基本数据类型,增强的for循环用法很简单,for (int i : arr)for后跟的括号里,是类型 变量名,然后跟:数组名,然后就可以使用变量名进行遍历。

而对于Java集合类的遍历,则和基本数据类型的数组类似

        LinkedList<Integer>list = new LinkedList<>();

        for(int i=0;i<100;i++) {
            list.add(i);
        }
        for (Integer integer : list) {
            System.out.println(integer);
        }

只不过类型变为了包装类,其他用法和以上一模一样。

了解完了其基本用法,我们来深挖下其背后的实现原理

有很多教材上说过,尽量使用增强的for循环,因为它更快速更安全,而又有些书中写到,编译器会自动把foreach循环变为for循环,那么问题来了,如果只是单纯的转换为for循环,为什么更安全性能更好呢?

我们来亲自看一下编译器到底做了什么,有下面这一段代码

        int[] arr = new int[100];

        for (int i : arr) {
//          System.out.println(i);
        }

javac编译以后得到字节码

         0: bipush        100
         2: newarray       int
         4: astore_1
         5: aload_1
         6: astore_2
         7: aload_2
         8: arraylength
         9: istore_3
        10: iconst_0
        11: istore        4
        13: iload         4
        15: iload_3
        16: if_icmpge     31
        19: aload_2
        20: iload         4
        22: iaload
        23: istore        5
        25: iinc          4, 1
        28: goto          13
        31: return

可以看到JVM首先会创建一个长度为100的int数组,然后有意思的事情来了,

        8: arraylength 
        9: istore_3

先取了数组长度

        10: iconst_0
        11: istore        4
        13: iload         4
        15: iload_3
        16: if_icmpge     31

然后有初始化了一个int值等于0,和arraylength比较大小,如果大于或等于他则跳转至31行

        25: iinc          4, 1
        28: goto          13

最后初始化的int值自增1,然后跳转至13行

很明显,这是一个for循环,而且是我们最常用的这种遍历形式,就和下面段一模一样

        for (int i = 0; i < arr.length; i++) {
            // do something
        }
也就是说对于基本类型数组的foreach循环,编译器最后会把它变为一个for循环,而且保障了其边界安全性,这也就是某些书上说编译器会把增强的for循环编译称为for循环的原因。

但是对于集合了的foreach循环,编译器会采用更优的一种手段。

        LinkedList<Integer> list = new LinkedList<>();
        for (Integer integer : list) {
        }

经过javac编译后可以得的:

         0: new           #2                  // class java/util/LinkedList
         3: dup
         4: invokespecial #3                  // Method java/util/LinkedList."<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method java/util/LinkedList.iterator:()Ljava/util/Iterator;
        12: astore_2
        13: aload_2
        14: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
        19: ifeq          35
        22: aload_2
        23: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
        28: checkcast     #7                  // class java/lang/Integer
        31: astore_3
        32: goto          13
        35: return

可以看到,前几行字节码依旧是JVM创建一个LinkedList对象
然后我们发现:

         9: invokevirtual #4                  // Method java/util/LinkedList.iterator:()Ljava/util/Iterator;

编译器自动调用了LinkedList.iterator()方法,得到了一个Iterator<Integer>对象的实例,并且把它存储在第三个本地变量。

        13: aload_2
        14: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
        19: ifeq          35

这里编译器取出第三个本地变量(Iterator<Integer>对象),然后调用了它的hasNext()方法,如果等于0(其实就是boolean的false),则跳转到35行

        23: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
        28: checkcast     #7                  // class java/lang/Integer
        31: astore_3
        32: goto          13

然后编译器又调用了Iterator<Integer>对象的next()方法,得到了一个Integer对象并检查其类型,跳转至13行。
可以看出这也是一个循环,如果转化成对应的JAVA代码如下:

        for(Iterator<Integer> I = list.iterator();I.hasNext();) {
            I.next();
        }
也就是说,对于集合类的增强for循环,编译器是调用了其中实现的Iterator类中的方法,这样做的好处是,对于性能更高,以ArrayListLinkedList为例,使用foreach循环遍历,其运行时间都是O(N),否则使用for循环配和get()方法遍历,LinkedList的时间复杂度是O(N*N)

由此可见,使用增强for循环是很有必要的,对于基本类型的数组类,它确实更安全,而对于集合类的遍历,他更安全更高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值