Как Java обрабатывает операторы IF и эффективность - PullRequest
6 голосов
/ 04 января 2012

Мне просто интересно, как на самом деле работает Java, когда дело доходит до if операторов.(Примечание: когда я говорю «компонент» ниже, я имею в виду отдельные части, проверяемые оператором, например, a, b, c)

Что является более эффективным с точки зрения расчетов?*

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

или

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

Причина, по которой я спрашиваю, заключается в том, что важно то, что делает Java в первой версии.Проверяет ли он каждую вещь в утверждении или проверяет a и, если это false, отменяет проверку остальной части утверждения?

Если это так, то имеет смысл поместитькомпонент, скорее всего, потерпит неудачу как первый компонент в заявлении.

Если весь оператор проверяется каждый раз, тогда имеет смысл разделить компоненты на несколько разных операторов, как во втором примере.

Ответы [ 9 ]

15 голосов
/ 04 января 2012

В Java гарантируется короткое замыкание && и ||: операнды вычисляются слева направо, и оценка останавливается, как только результат известен с уверенностью.

Из JLS :

Оператор && подобен & (§15.22.2), , но оценивает свой правый операнд, только если значениеего левый операнд - true. Синтаксически левоассоциативен (он группируется слева направо).

Оператор || похож на | (§15.22.2), , но оценивает его правый операнд, только если значение его левого операнда равно false. Itсинтаксически левоассоциативен (он группируется слева направо).

Это означает, что два фрагмента кода в вашем вопросе в точности эквивалентны.

6 голосов
/ 04 января 2012

Эти фрагменты кода полностью эквивалентны и выдают точно такой же байт-код:

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.

5 голосов
/ 04 января 2012

Мне было любопытно, как компилятор будет обрабатывать оба случая, поэтому я скомпилировал следующий код, используя 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");
    }
}

Полагаю, не имеет значения, каким образом вы его напишите.

5 голосов
/ 04 января 2012

для (a && b && c), условное «короткое замыкание», означающее, что при выходе из строя одно не учитывается.

Однако, если получение a, b и c не будет слишком дорогим, это может быть причиной преждевременной оптимизации.

Если некоторые или все стоят дорого, то если вы сделаете

if (getA() && getB() && getC())...

тогда, если getA() завершится неудачно, другие методы даже не будут вызваны.

3 голосов
/ 04 января 2012

Лучший способ проверить подобные вещи - посмотреть в Спецификации языка Java .

В этом случае вы хотите раздел 15.23 :

Во время выполнения выражение левого операнда вычисляется первым; если результат имеет тип Boolean, он подвергается распаковке преобразования (§5.1.8); если результирующее значение равно false, значение условного выражения and равно 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;

(Стоит знать, какое поведение имеет отношение к какой части языка, когда вы хотите обратиться к спецификации.)

2 голосов
/ 04 января 2012

В первом случае if оценивается как false при первом обнаруженном false состоянии (оно закорачивается).В противном случае вы не сможете сделать что-то вроде:

if(a != null && a.toString().equals("b"))

без получения NullPointerException, если вторая часть также будет оценена.

1 голос
/ 04 января 2012

&& и ||являются логическими операторами короткого замыкания.Это означает, что в вашем примере, если a равно false, остальная часть выражения не будет оценена.

Если вы хотите использовать логические операторы без короткого замыкания, вы можете использовать & и |.

Вы можете посмотреть следующее утверждение Спецификации языка Java, если вам нужно подтверждение: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.22.2

1 голос
/ 04 января 2012

Это:

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 }
}
1 голос
/ 04 января 2012
if (a && b && c) { do stuff }

Это имеет короткое замыкание. && произойдет короткое замыкание, если a оценивается как ложное, что означает, что остальная часть проверки не выполнена.

Кроме этого, я думаю, что нет никакой разницы между двумя подходами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...