概述
首先我们来认识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
类中的方法,这样做的好处是,对于性能更高,以ArrayList
和LinkedList
为例,使用foreach循环遍历,其运行时间都是O(N)
,否则使用for
循环配和get()
方法遍历,LinkedList
的时间复杂度是O(N*N)
由此可见,使用增强for循环是很有必要的,对于基本类型的数组类,它确实更安全,而对于集合类的遍历,他更安全更高效。
最后
以上就是犹豫鞋垫为你收集整理的Java增强for循环--foreach深入探讨的全部内容,希望文章能够帮你解决Java增强for循环--foreach深入探讨所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复