在Java循环中,使用boolean
标志而不是if
语句更有效吗?
看看这两段代码。
使用旗帜:
public boolean isSomethingForAnyone() {
boolean flag = false;
for (Item item : listOfItems) {
flag = flag || item.isSomething();
}
return flag;
}
使用if
声明:
public boolean isSomethingForAnyone() {
for (Item item : listOfItems) {
if (item.isSomething())
return true;
}
return false;
}
如果if
在第一次迭代时返回isSomething()
,那么带有true
语句的方法当然会更快。但是,平均来说它更快还是分支减慢到足够慢以至于它更慢?另外,如果循环更快,情况会有所不同吗?为了简单起见,我在这里使用了for-each循环,我认为这比使用计数器迭代数组要慢。
答案 0 :(得分:4)
这两段代码并不完全相同。
即使您只需要根据需要多次调用item.isSomething()
(与我的原始答案相反),第一个版本仍会继续尝试迭代其余的集合。
想象一下Item.isSomething()
的一个实现,它修改了找到该项的集合(如果它返回true
)。那时,第一段代码会抛出一个ConcurrentModificationException
,假设它是一个“常规”集合 - 而第二段代码只返回true
。
从根本上说,第二段代码更有效:它只迭代确定答案所需的列表,而不是通过所有内容。很可能性能不同是无关紧要的 - 特别是如果集合很小 - 但这取决于上下文。
您发现更多可读是另一回事 - 效率可能不会很高,尽管这取决于上下文。就个人而言,我发现第二个版本更易读以及更高效,所以我总是使用它。 (好吧,我会在if
语句的主体周围添加大括号,但就是这样。)
答案 1 :(得分:0)
你这个循环字面上十亿次?如果没有,差异可能无法衡量。
你可以查看生成的字节码,看看究竟发生了什么,但编译器和jit以及vm本身可能会优化掉任何差异。
答案 2 :(得分:0)
如果您想知道机器上哪个“更快”,请运行它。
如果我们想知道哪些需要执行更多指令,那么我们可以查看字节码。
对于第一种方法,我们得到了这个(调用javap -c
)
Code:
0: iconst_0
1: istore_1
2: getstatic #2 // Field listOfItems:Ljava/util/List;
5: invokeinterface #3, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
10: astore_2
11: aload_2
12: invokeinterface #4, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
17: ifeq 50
20: aload_2
21: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
26: checkcast #6 // class Item
29: astore_3
30: iload_1
31: ifne 41
34: aload_3
35: invokevirtual #7 // Method Item.isSomething:()Z
38: ifeq 45
41: iconst_1
42: goto 46
45: iconst_0
46: istore_1
47: goto 11
50: iload_1
51: ireturn
我们对循环的内部感兴趣,即第29-46行(第11-26行是迭代器的东西)。所以大约有10条指令。
对于第二种方法,我们得到了这个:
Code:
0: getstatic #2 // Field listOfItems:Ljava/util/List;
3: invokeinterface #3, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
8: astore_1
9: aload_1
10: invokeinterface #4, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
15: ifeq 40
18: aload_1
19: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
24: checkcast #6 // class Item
27: astore_2
28: aload_2
29: invokevirtual #7 // Method Item.isSomething:()Z
32: ifeq 37
35: iconst_1
36: ireturn
37: goto 9
40: iconst_0
41: ireturn
感兴趣的线路是27-37。所以7条指令。
从数字的角度来看,第二种方法是最重要的(请注意,我们假设所有堆栈操作都需要相同的时间来执行)。