定义一个“for-each循环”,最好使用以下约定:
情况1
for (String s : xxx.getList())
这是第二种情况:
List<String> list = xxx.getList();
for (String s : list)
在情况1中,方法getList()会被调用多少次?是每个循环都会调用一次还是只在开始时调用一次?谢谢。
while(x < file.length()) ...
而不是size = file.length(); while(x < file.length())
。 这个 getList()
方法可能是一个非常昂贵的操作。 - slim你可以这样测试:
public static void main(tring[] args) {
for (String string : getList()) {
System.out.println(string);
}
}
private static List<String> getList() {
System.out.println("getList");
List<String> l = new ArrayList<String>();
l.add("a");
l.add("b");
return l;
}
...你会发现getList()只被调用了一次。
增强型for循环的语法如下:
for(Type item : iterable) ...
getList()
在运行时返回一个Iterable
--在这种情况下是一个List
。一旦返回了List
,循环就可以使用它所需的唯一Iterable
。 for (String string : getList()) ...
并且
List list = getList();
for (String string : list) ...
...是等价的。第一种形式的优点是简短明了。第二种形式的优点是,如果需要,您可以在之后再次使用列表。
在Eclipse中,您可以通过自动重构(其他IDE也有类似功能)在两种形式之间切换:
从第一种形式开始,选择getList()
,右键单击,选择重构
-> 提取局部变量
。Eclipse会提示您输入一个变量名。输入list
,它将为您创建第二种形式。
从第二种形式开始,在for()
语句中选择list
,右键单击,选择重构
-> 内联
。它会提示您,然后将其改回旧格式。
重构应该产生功能上相同的代码,因此您可以将其用作这两种形式等效的证据。
但要注意,其他循环形式并不像这么聪明。
while( size < file.length()) {
...
}
...每次循环迭代时都会执行file.length()
,而
long fileLength = file.length();
while( size < fileLength ) {
...
}
... 仅执行一次 file.length()
。传统的 for(;;)
循环也是如此。
上面描述的 Eclipse 重构转换也会在这两种形式之间切换,但行为不同。
Iterable
(或数组)来获取一个Iterator
以循环遍历项。Iterator
仅被获取一次。for (String s : list) {
// loop code
}
等同于:
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
// loop code
}
for (String s : list)
编译后
for (Iterator<String> i = list.iterator(); i.hasNext(); ){...}
for (String s : xxx.getList()){...}
编译后会是:
for (Iterator<String> i = xxx.getList().iterator(); i.hasNext(); ){...}
这意味着第二种情况下getList
只会被调用一次。
List<String> l = getList();
for (String s : l) {
processItem(s);
}
字节码是:
0: invokestatic #4; //Method getList:()Ljava/util/List;
3: astore_1
4: aload_1
5: invokeinterface #5, 1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
10: astore_2
11: aload_2
12: invokeinterface #6, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
17: ifeq 37
20: aload_2
21: invokeinterface #7, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
26: checkcast #8; //class java/lang/String
29: astore_3
30: aload_3
31: invokestatic #9; //Method processItem:(Ljava/lang/String;)V
34: goto 11
37: return
而对于这段代码:
for (String s : getList()) {
processItem(s);
}
字节码是:
0: invokestatic #4; //Method getList:()Ljava/util/List;
3: invokeinterface #5, 1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
8: astore_1
9: aload_1
10: invokeinterface #6, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
15: ifeq 35
18: aload_1
19: invokeinterface #7, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
24: checkcast #8; //class java/lang/String
27: astore_2
28: aload_2
29: invokestatic #9; //Method processItem:(Ljava/lang/String;)V
32: goto 9
35: return
这两个字节码之间唯一的区别在于,在前一个字节码中,我们有:
3: astore_1
4: aload_1
这相当于将getList()
方法返回的结果存储在一个变量中。换句话说,这两种for循环风格的性能几乎相同(除了用于存储getList()
方法返回结果的变量之外)。
getList方法在每种情况下都被调用相同的次数 - 只调用一次。
至于采用哪种风格,这取决于情况。如果您需要在for循环之后再次引用列表,请选择Case 2,以避免不必要地多次调用getList。
从调试的角度来看,我也更喜欢Case 2。您可以在for循环上设置断点,并在迭代开始之前检查列表的内容。
getList()
每次返回不同的内容会怎么样? ;) - Rob Audenaerdefor(;;)
而不是增强的for(T item: Iterator<T> iterator)
)。已投票重新开放。 - slim