我很好奇Java在if
语句中的实际工作方式。 (注意:当我说下面的“组件”时,我指的是语句检查的个别部分,例如a
,b
,c
)
在计算方面哪个更有效?
if (a && b && c) { do stuff }
或
if (a) {
if (b) {
if (c) {
do stuff }
}
}
我问的原因是因为Java在第一个版本中的作用很重要。它是检查语句中的每一件事还是检查a
,如果它是false
,那么取消检查语句的其余部分?
如果是这种情况,那么将最有可能失败的组件作为语句中的第一个组件是有意义的。
如果每次都检查整个语句,那么将组件拆分成一堆不同的语句更有意义,如第二个例子所示。
答案 0 :(得分:15)
在Java中,&&
和||
保证短路:操作数从左到右进行评估,一旦确定结果,评估就会停止。
来自JLS:
&&
运算符类似于&
(第15.22.2节),但仅在其左侧操作数的值为true
时才计算其右侧操作数它在语法上是左关联的(它从左到右分组)。
||
运算符类似于|
(第15.22.2节),但仅在其左侧操作数的值为false时才计算其右侧操作数。它在语法上是左关联的(它从左到右分组)。
这意味着您问题中的两个代码段完全相同。
答案 1 :(得分:6)
这些代码片段完全等效,并生成完全相同的字节码:
0: iload_1
1: ifeq 20 //a == false (0)
4: iload_2
5: ifeq 20 //b == false (0)
8: iload_3
9: ifeq 20 //c == false (0)
12: //do stuff
20: return
虽然解释不同,但代码生成器产生相同的输出。在第一种情况下,由于惰性评估,后续术语如果前一个是false
则不被评估。在第二种情况下,如果不满足周围if
条件,运行时将不会更深。
答案 2 :(得分:5)
对于(a && b && c)
,条件为“短路”,意味着一旦失败,其余的就不会被评估。
但是,除非获得a
,b
和c
的价格昂贵,否则可能会出现过早优化的情况。
如果部分或全部 昂贵,那么如果你这样做
if (getA() && getB() && getC())...
然后如果getA()
失败,则甚至不会调用其他方法。
答案 3 :(得分:5)
我很好奇编译器如何处理这两种情况,因此我使用Java 1.6编译了以下代码:
public class IfTest
{
public static void main(String[] args)
{
IfTest iffy = new IfTest();
iffy.doit();
}
private void doit()
{
Random rand = new Random();
boolean a = rand.nextBoolean();
boolean b = rand.nextBoolean();
boolean c = rand.nextBoolean();
if (a && b && c)
{
System.out.println("block 1");
}
if (a)
{
if (b)
{
if (c)
{
System.out.println("block 2");
}
}
}
}
}
...然后使用Jad对其进行反编译。以下是反编译器从类文件中生成的内容:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: IfTest.java
import java.util.Random;
public class IfTest
{
public IfTest()
{
}
public static void main(String args[])
{
IfTest iffy = new IfTest();
iffy.doit();
}
private void doit()
{
Random rand = new Random();
boolean a = rand.nextBoolean();
boolean b = rand.nextBoolean();
boolean c = rand.nextBoolean();
if(a && b && c)
System.out.println("block 1");
if(a && b && c)
System.out.println("block 2");
}
}
我想你写它的方式并不重要。
答案 4 :(得分:3)
检查此类事情的最佳方法是查看Java Language Specification。
在这种情况下,您需要section 15.23:
在运行时,首先计算左侧操作数表达式;如果结果具有布尔类型,则进行拆箱转换(第5.1.8节);如果结果值为false,则条件和表达式的值为false,并且不评估右侧操作数表达式。
所以不,在表达式a && b && c
中,如果b
评估为c
,则不评估a
和false
。
请注意,这不是if
语句行为的一部分 - 它是&&
运算符行为的一部分。你可以在其他地方使用表达式看到相同的效果:
// Still won't evaluate b and c if a is false...
boolean x = a && b && c;
(值得注意的是,当您想要查阅规范时,该语言的哪一部分会导致什么行为。)
答案 5 :(得分:2)
在第一种情况下,if
在第一次遇到false
条件(它短路)时评估为false
。否则你将无法做到这样的事情:
if(a != null && a.toString().equals("b"))
如果第二部分也将被评估,则不会获得NullPointerException
。
答案 6 :(得分:1)
if (a && b && c) { do stuff }
那有短路。如果&&
评估为false,a
将会短路,这意味着其余的检查都没有完成。
除此之外,我认为这两种方法没有区别。
答案 7 :(得分:1)
此:
if (condition is met) {
//continute to next check
if (condition is met) {
//continue to next check
if (condition is met) {
do stuff }
}
}
与if (a && b && c) { do stuff }
相同,但占用大量空间。您不必担心这个特定代码段的效率,所以选择第一个选项是最好的选择。你也可以这样做
if (a && b){
if (condition is met) {
do stuff }
}
答案 8 :(得分:1)
&安培;&安培;和||是短路逻辑运算符。这意味着在您的示例中,如果a为false,则不会对表达式的其余部分进行求值。
如果您需要非短路逻辑运算符,可以使用&和|。
如果需要确认,可以查看以下Java语言规范声明:http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.22.2