美文网首页
Java中Foreach原理

Java中Foreach原理

作者: sunpy | 来源:发表于2018-09-26 14:02 被阅读37次

    介绍

    对于之前的for循环在jvm中如何实现
    9. jvm9:字节码指令,我进行了思考分析,而对于我们日常开发却经常习惯写的是Foreach语句,对于Foreach语句的本质,我们需要深入了解一下。

    数组中的Foreach

    public class MyTest {
        
        public static void main(String[] args) {
            int[] arr = new int[10];
            arr[0] = 1;
            
            for (int i : arr) {
                System.out.println(i);
            }
        }
    }
    

    javap -verbose MyTest.class查看:

    C:\Users\Administrator\Desktop>javap -verbose MyTest.class
    Classfile /C:/Users/Administrator/Desktop/MyTest.class
      Last modified 2018-9-26; size 664 bytes
      MD5 checksum 16772f3ceb324e67bfb14f4271c0fe3e
      Compiled from "MyTest.java"
    public class cn.spy.spring.test.MyTest
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Class              #2             // cn/spy/spring/test/MyTest
       #2 = Utf8               cn/spy/spring/test/MyTest
       #3 = Class              #4             // java/lang/Object
       #4 = Utf8               java/lang/Object
       #5 = Utf8               <init>
       #6 = Utf8               ()V
       #7 = Utf8               Code
       #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
       #9 = NameAndType        #5:#6          // "<init>":()V
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcn/spy/spring/test/MyTest;
      #14 = Utf8               main
      #15 = Utf8               ([Ljava/lang/String;)V
      #16 = Fieldref           #17.#19        // java/lang/System.out:Ljava/io/Print
    Stream;
      #17 = Class              #18            // java/lang/System
      #18 = Utf8               java/lang/System
      #19 = NameAndType        #20:#21        // out:Ljava/io/PrintStream;
      #20 = Utf8               out
      #21 = Utf8               Ljava/io/PrintStream;
      #22 = Methodref          #23.#25        // java/io/PrintStream.println:(I)V
      #23 = Class              #24            // java/io/PrintStream
      #24 = Utf8               java/io/PrintStream
      #25 = NameAndType        #26:#27        // println:(I)V
      #26 = Utf8               println
      #27 = Utf8               (I)V
      #28 = Utf8               args
      #29 = Utf8               [Ljava/lang/String;
      #30 = Utf8               arr
      #31 = Utf8               [I
      #32 = Utf8               i
      #33 = Utf8               I
      #34 = Utf8               StackMapTable
      #35 = Class              #29            // "[Ljava/lang/String;"
      #36 = Class              #31            // "[I"
      #37 = Utf8               SourceFile
      #38 = Utf8               MyTest.java
    {
      public cn.spy.spring.test.MyTest();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #8                  // Method java/lang/Object."<init>
    ":()V
             4: return
          LineNumberTable:
            line 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcn/spy/spring/test/MyTest;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=6, args_size=1
             0: bipush        10
             2: newarray       int
             4: astore_1
             5: aload_1
             6: iconst_0
             7: iconst_1
             8: iastore
             9: aload_1
            10: dup
            11: astore        5
            13: arraylength
            14: istore        4
            16: iconst_0
            17: istore_3
            18: goto          36
            21: aload         5
            23: iload_3
            24: iaload
            25: istore_2
            26: getstatic     #16                 // Field java/lang/System.out:Ljav
    a/io/PrintStream;
            29: iload_2
            30: invokevirtual #22                 // Method java/io/PrintStream.prin
    tln:(I)V
            33: iinc          3, 1
            36: iload_3
            37: iload         4
            39: if_icmplt     21
            42: return
          LineNumberTable:
            line 7: 0
            line 8: 5
            line 10: 9
            line 11: 26
            line 10: 33
            line 13: 42
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      43     0  args   [Ljava/lang/String;
                5      38     1   arr   [I
               26       7     2     i   I
          StackMapTable: number_of_entries = 2
            frame_type = 255 /* full_frame */
              offset_delta = 21
              locals = [ class "[Ljava/lang/String;", class "[I", top, int, int, cla
    ss "[I" ]
              stack = []
            frame_type = 14 /* same */
    }
    SourceFile: "MyTest.java"
    

    说明:
    可以看到jvm使用了命令if_icmplt,这是一个比较、跳转结合的命令来实现我们对于数组的Foreach命令。譬如arraylength这种命令就是获取数组的引用,jvm会获取数组的长度,然后将数组的长度值压入操作数栈,后面通过if_icmplt比较栈顶递增的i值(iinc)和栈顶数组长度值,可以看出和for循环的下标遍历无区别。

    ArrayList中的Foreach

    public class ForeachTest {
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("0");
            
            for (String str : list) {
                System.out.println(str);
            }
        }
    }
    

    javap -verbose ForeachTest.class查看:

    C:\Users\Administrator\Desktop>javap -verbose ForeachTest.class
    Classfile /C:/Users/Administrator/Desktop/ForeachTest.class
      Last modified 2018-9-26; size 1073 bytes
      MD5 checksum e3e830f354ea6bec0dd621a954ffc2c9
      Compiled from "ForeachTest.java"
    public class cn.spy.spring.test.ForeachTest
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Class              #2             // cn/spy/spring/test/ForeachTest
       #2 = Utf8               cn/spy/spring/test/ForeachTest
       #3 = Class              #4             // java/lang/Object
       #4 = Utf8               java/lang/Object
       #5 = Utf8               <init>
       #6 = Utf8               ()V
       #7 = Utf8               Code
       #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
       #9 = NameAndType        #5:#6          // "<init>":()V
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcn/spy/spring/test/ForeachTest;
      #14 = Utf8               main
      #15 = Utf8               ([Ljava/lang/String;)V
      #16 = Class              #17            // java/util/ArrayList
      #17 = Utf8               java/util/ArrayList
      #18 = Methodref          #16.#9         // java/util/ArrayList."<init>":()V
      #19 = String             #20            // 0
      #20 = Utf8               0
      #21 = InterfaceMethodref #22.#24        // java/util/List.add:(Ljava/lang/Obje
    ct;)Z
      #22 = Class              #23            // java/util/List
      #23 = Utf8               java/util/List
      #24 = NameAndType        #25:#26        // add:(Ljava/lang/Object;)Z
      #25 = Utf8               add
      #26 = Utf8               (Ljava/lang/Object;)Z
      #27 = InterfaceMethodref #22.#28        // java/util/List.iterator:()Ljava/uti
    l/Iterator;
      #28 = NameAndType        #29:#30        // iterator:()Ljava/util/Iterator;
      #29 = Utf8               iterator
      #30 = Utf8               ()Ljava/util/Iterator;
      #31 = InterfaceMethodref #32.#34        // java/util/Iterator.next:()Ljava/lan
    g/Object;
      #32 = Class              #33            // java/util/Iterator
      #33 = Utf8               java/util/Iterator
      #34 = NameAndType        #35:#36        // next:()Ljava/lang/Object;
      #35 = Utf8               next
      #36 = Utf8               ()Ljava/lang/Object;
      #37 = Class              #38            // java/lang/String
      #38 = Utf8               java/lang/String
      #39 = Fieldref           #40.#42        // java/lang/System.out:Ljava/io/Print
    Stream;
      #40 = Class              #41            // java/lang/System
      #41 = Utf8               java/lang/System
      #42 = NameAndType        #43:#44        // out:Ljava/io/PrintStream;
      #43 = Utf8               out
      #44 = Utf8               Ljava/io/PrintStream;
      #45 = Methodref          #46.#48        // java/io/PrintStream.println:(Ljava/
    lang/String;)V
      #46 = Class              #47            // java/io/PrintStream
      #47 = Utf8               java/io/PrintStream
      #48 = NameAndType        #49:#50        // println:(Ljava/lang/String;)V
      #49 = Utf8               println
      #50 = Utf8               (Ljava/lang/String;)V
      #51 = InterfaceMethodref #32.#52        // java/util/Iterator.hasNext:()Z
      #52 = NameAndType        #53:#54        // hasNext:()Z
      #53 = Utf8               hasNext
      #54 = Utf8               ()Z
      #55 = Utf8               args
      #56 = Utf8               [Ljava/lang/String;
      #57 = Utf8               list
      #58 = Utf8               Ljava/util/List;
      #59 = Utf8               str
      #60 = Utf8               Ljava/lang/String;
      #61 = Utf8               LocalVariableTypeTable
      #62 = Utf8               Ljava/util/List<Ljava/lang/String;>;
      #63 = Utf8               StackMapTable
      #64 = Class              #56            // "[Ljava/lang/String;"
      #65 = Utf8               SourceFile
      #66 = Utf8               ForeachTest.java
    {
      public cn.spy.spring.test.ForeachTest();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #8                  // Method java/lang/Object."<init>
    ":()V
             4: return
          LineNumberTable:
            line 6: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcn/spy/spring/test/ForeachTest;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=4, args_size=1
             0: new           #16                 // class java/util/ArrayList
             3: dup
             4: invokespecial #18                 // Method java/util/ArrayList."<in
    it>":()V
             7: astore_1
             8: aload_1
             9: ldc           #19                 // String 0
            11: invokeinterface #21,  2           // InterfaceMethod java/util/List.
    add:(Ljava/lang/Object;)Z
            16: pop
            17: aload_1
            18: invokeinterface #27,  1           // InterfaceMethod java/util/List.
    iterator:()Ljava/util/Iterator;
            23: astore_3
            24: goto          44
            27: aload_3
            28: invokeinterface #31,  1           // InterfaceMethod java/util/Itera
    tor.next:()Ljava/lang/Object;
            33: checkcast     #37                 // class java/lang/String
            36: astore_2
            37: getstatic     #39                 // Field java/lang/System.out:Ljav
    a/io/PrintStream;
            40: aload_2
            41: invokevirtual #45                 // Method java/io/PrintStream.prin
    tln:(Ljava/lang/String;)V
            44: aload_3
            45: invokeinterface #51,  1           // InterfaceMethod java/util/Itera
    tor.hasNext:()Z
            50: ifne          27
            53: return
          LineNumberTable:
            line 9: 0
            line 10: 8
            line 12: 17
            line 13: 37
            line 12: 44
            line 15: 53
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      54     0  args   [Ljava/lang/String;
                8      46     1  list   Ljava/util/List;
               37       7     2   str   Ljava/lang/String;
          LocalVariableTypeTable:
            Start  Length  Slot  Name   Signature
                8      46     1  list   Ljava/util/List<Ljava/lang/String;>;
          StackMapTable: number_of_entries = 2
            frame_type = 255 /* full_frame */
              offset_delta = 27
              locals = [ class "[Ljava/lang/String;", class java/util/List, top, cla
    ss java/util/Iterator ]
              stack = []
            frame_type = 16 /* same */
    }
    SourceFile: "ForeachTest.java"
    

    说明:
    jvm在ArrayList的遍历上没有使用iinc自增下标和求数组长度在栈顶比较跳转,而是采用了invokeinterface命令调用Iterator接口的hasNext方法进行判断,使用ifne命令进行比较不等于0就进行跳转。

    总结

    1. 数组中的Foreach和下标的for循环没区别,都是采用自增下标和数组长度比较循环的。
    2. ArrayList集合中的Foreach是采用Iterator接口的hasNext方法来实现判断的,ArrayList中的内部类通过实现Iterator接口,来实现对集合ArrayList的迭代,而jvm具体是通过ifne命令来判断不等于0则跳转继续循环。

    相关文章

      网友评论

          本文标题:Java中Foreach原理

          本文链接:https://www.haomeiwen.com/subject/wcvdoftx.html