Java增强for循环--foreach深入探讨
2018-03-09 17:02
465 查看
首先我们来认识Java中的foreach,foreach也被称为增强的for循环
我们来看一下他的基本用法
对于基本数据类型的数组遍历有如下用法:
可以看出,对于基本数据类型,增强的for循环用法很简单,
而对于Java集合类的遍历,则和基本数据类型的数组类似
只不过类型变为了包装类,其他用法和以上一模一样。
javac编译以后得到字节码
可以看到JVM首先会创建一个长度为100的
先取了数组长度
然后有初始化了一个
最后初始化的
很明显,这是一个for循环,而且是我们最常用的这种遍历形式,就和下面段一模一样
经过javac编译后可以得的:
可以看到,前几行字节码依旧是JVM创建一个
然后我们发现:
编译器自动调用了
这里编译器取出第三个本地变量(
然后编译器又调用了
可以看出这也是一个循环,如果转化成对应的JAVA代码如下:
也就是说,对于集合类的增强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
类中的方法,这样做的好处是,对于性能更高,以ArrayList
和LinkedList
为例,使用foreach循环遍历,其运行时间都是O(N)
,否则使用for
循环配和get()
方法遍历,LinkedList
的时间复杂度是O(N*N)
由此可见,使用增强for循环是很有必要的,对于基本类型的数组类,它确实更安全,而对于集合类的遍历,他更安全更高效。
相关文章推荐
- java基础39 增强for循环(也叫foreach循环)
- Java基础之高级(增强的)For循环(foreach)
- Java源码分析:深入探讨Iterator模式及Java 5.0中改进的for循环
- Java源码分析:深入探讨Iterator模式及Java 5.0中改进的for循环
- Java源码分析:深入探讨Iterator模式及Java 5.0中改进的for循环
- Java中的增强for循环(foreach)的实现原理
- 深入探讨array_map、foreach、for循环处理数组的效率
- 03 深入了解Java集合之增强for循环
- Java基础——增强for循环(foreach)
- java1.5以上增强的for循环 与foreach
- Java基础笔记 – 增强的for循环For each循环 自动装箱拆箱 可变参数
- java基础--深入探讨 Java 类加载器--01
- 深入探讨 Java 类加载器
- Java中增强for循环
- 深入探讨Java字符串的拼接
- Java_容器_Collection_增强for循环
- Java高级特性之增强for循环(一)
- Java强引用、软引用、弱引用及虚引用深入探讨
- 深入探讨 Java 类加载器(转)
- 深入探讨 Java 类加载器