回答你的第一个问题:不,这三个循环不等价。其次,在这些循环中找不到空值检查;试图遍历不存在的内容是没有意义的。
假设我们有以下类:
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class EnhancedFor {
private List<Integer> dummyList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
private List<Integer> nullList = null;
public void enhancedForDummyList() {
for(Integer i : dummyList) {
System.out.println(i);
}
}
public void iteratorDummyList() {
for(Iterator<Integer> iterator = dummyList.iterator(); iterator.hasNext();) {
System.out.println(iterator.next());
}
}
public void normalLoopDummyList() {
for(int i = 0; i < dummyList.size(); i++) {
System.out.println(dummyList.get(i));
}
}
}
我们将对其进行字节码分解,看看这些循环之间是否有任何区别。
1:增强型for循环 vs. 迭代器
这是增强型for循环的字节码。
public enhancedForDummyList()V
L0
LINENUMBER 12 L0
ALOAD 0
GETFIELD EnhancedFor.dummyList : Ljava/util/List;
INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator;
ASTORE 1
L1
FRAME APPEND [java/util/Iterator]
ALOAD 1
INVOKEINTERFACE java/util/Iterator.hasNext ()Z
IFEQ L2
ALOAD 1
INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
CHECKCAST java/lang/Integer
ASTORE 2
L3
LINENUMBER 13 L3
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 2
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L4
LINENUMBER 14 L4
GOTO L1
L2
LINENUMBER 15 L2
FRAME CHOP 1
RETURN
L5
LOCALVARIABLE i Ljava/lang/Integer; L3 L4 2
LOCALVARIABLE i$ Ljava/util/Iterator; L1 L2 1
LOCALVARIABLE this LEnhancedFor; L0 L5 0
MAXSTACK = 2
MAXLOCALS = 3
以下是迭代器的字节码。
public iteratorDummyList()V
L0
LINENUMBER 24 L0
ALOAD 0
GETFIELD EnhancedFor.dummyList : Ljava/util/List;
INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator;
ASTORE 1
L1
FRAME APPEND [java/util/Iterator]
ALOAD 1
INVOKEINTERFACE java/util/Iterator.hasNext ()Z
IFEQ L2
L3
LINENUMBER 25 L3
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
GOTO L1
L2
LINENUMBER 27 L2
FRAME CHOP 1
RETURN
L4
LOCALVARIABLE iterator Ljava/util/Iterator; L1 L2 1
LOCALVARIABLE this LEnhancedFor; L0 L4 0
MAXSTACK = 2
MAXLOCALS = 2
总的来说,它看起来像他们在做非常相似的事情。 他们使用相同的接口。不同之处在于增强型for循环使用当前值(i
)和指向列表其余部分的光标(i$
)这两个变量,而迭代器只需要光标来调用.next()
。
类似,但不完全相同。
2. 增强型for循环 vs for循环
让我们添加for循环的字节码。
public normalLoopDummyList()V
L0
LINENUMBER 24 L0
ICONST_0
ISTORE 1
L1
FRAME APPEND [I]
ILOAD 1
ALOAD 0
GETFIELD EnhancedFor.dummyList : Ljava/util/List;
INVOKEINTERFACE java/util/List.size ()I
IF_ICMPGE L2
L3
LINENUMBER 25 L3
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 0
GETFIELD EnhancedFor.dummyList : Ljava/util/List;
ILOAD 1
INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L4
LINENUMBER 24 L4
IINC 1 1
GOTO L1
L2
LINENUMBER 27 L2
FRAME CHOP 1
RETURN
L5
LOCALVARIABLE i I L1 L2 1
LOCALVARIABLE this LEnhancedFor; L0 L5 0
MAXSTACK = 3
MAXLOCALS = 2
它正在做一些不同的事情。 它根本没有使用Iterator
接口。 相反,我们在调用get()
,这个方法只由List
指定,而不是Iterator
。
3. 结论
有一个有效的理由解释为什么我们要假设我们正在取消引用的列表不为空 - 我们正在调用由接口指定的方法。 如果那些方法未被实现,那将是不同的情况:抛出UnsupportedOperationException
。如果我们试图在不存在的对象上调用契约,那就毫无意义了。
arr
不是null
,您是否希望它再进行一次无用的null
检查呢?我想答案是否定的。 - Alvin Wong