Java如何处理IF语句和效率

时间:2012-01-04 17:16:16

标签: java if-statement

我很好奇Java在if语句中的实际工作方式。 (注意:当我说下面的“组件”时,我指的是语句检查的个别部分,例如abc

在计算方面哪个更有效?

if (a && b && c) { do stuff }

if (a) {
  if (b) {
    if (c) {
    do stuff }
  }
}

我问的原因是因为Java在第一个版本中的作用很重要。它是检查语句中的每一件事还是检查a,如果它是false,那么取消检查语句的其余部分?

如果是这种情况,那么将最有可能失败的组件作为语句中的第一个组件是有意义的。

如果每次都检查整个语句,那么将组件拆分成一堆不同的语句更有意义,如第二个例子所示。

9 个答案:

答案 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),条件为“短路”,意味着一旦失败,其余的就不会被评估。

但是,除非获得abc的价格昂贵,否则可能会出现过早优化的情况。

如果部分或全部 昂贵,那么如果你这样做

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,则不评估afalse

请注意,这不是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