美文网首页
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原理

    介绍 对于之前的for循环在jvm中如何实现9. jvm9:字节码指令,我进行了思考分析,而对于我们日常开发却经常...

  • Java foreach 原理

    from:http://stackoverflow.com/questions/85190/how-does-th...

  • Java foreach 循环原理

    foreach是java的语法糖,所谓语法糖就是通过编译器或者其它手段优化了代码,给使用带来了遍历。比如,没有fo...

  • Java中foreach循环的实现原理

    1、背景知识介绍 java foreach 语法是在jdk1.5时加入的新特性,主要是当作for语法的一个增强,那...

  • Java 几种循环方式

    1、java循环分类 Iterator、for、foreach、Stream.forEach 2、java迭代器I...

  • 迭代器模式

    java中的迭代器想必大家都用过,但是大家知道他的性能以及实现原理吗?今天就来介绍一波实际上foreach封装了迭...

  • 怎么break java8 stream的foreach

    怎么break java8 stream的foreach 简介 我们通常需要在java stream中遍历处理里面...

  • 数组 6-8:使用foreach操作数组

    使用 foreach 操作数组 foreach 并不是 Java 中的关键字,是 for 语句的特殊简化版本,在遍...

  • java中 使用 foreach 操作数组

    使用 foreach 操作数组 foreach 并不是 Java 中的关键字,是 for 语句的特殊简化版本,在遍...

  • foreach原理

    纯粹是个人学习总结,如有不对的地方请吐槽。 在平时Java程序中,应用比较多的就是对Collection集合类的f...

网友评论

      本文标题:Java中Foreach原理

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