Яка конструкція “якщо” є швидшою - оператор або трикутний оператор?


83

Є два типи ifоператорів в Java - класичні: if {} else {}і скорочений: exp ? value1 : value2. Один швидший за інший чи вони однакові?

заява:

int x;
if (expression) {
  x = 1;
} else {
  x = 2;
}

потрійний оператор:

int x = (expression) ? 1 : 2;

34
Я здогадуюсь, що немає абсолютно ніякої різниці. Це просто синтаксис. Якщо компілятори не є чимось злим (або чимось іншим), і я помиляюся
sinelaw

4
Ви (мікро) тестували його? Поділіться результатами.
BalusC

3
Обидва отримають jit'ed. Різниці взагалі не буде. І не заважайте розкладати речі. Перше, що робить HotSpot, - це видалити всі оптимізації, застосовані javac.
Ivo Wetzel

11
Вони не існують для різних швидкостей. Вони існують для різних цілей. Я впевнений, ви розумієте різницю між твердженнями та виразами. Заяви виконують дії. Вирази породжують значення. ifпризначений для використання в операторах. ?призначений для використання у виразах.
Mike Dunlavey

3
+1, оскільки відповіді на це запитання варто прочитати, навіть якщо наміри оригінального запитання неправильні.
jball

Відповіді:


106

Там є лише один тип твердження "якщо". Інший - умовний вираз. Щодо того, які будуть ефективнішими: вони можуть скомпілюватись до одного і того ж байт-коду, і я би очікував, що вони будуть поводитися однаково - або настільки близько, що ви точно не захочете вибирати одне з іншого з точки зору продуктивності.

Іноді ifвираз буде читабельнішим, інколи умовний оператор буде читабельнішим. Зокрема, я б рекомендував використовувати умовний оператор, коли два операнди прості та не мають побічних ефектів, тоді як, якщо основною метою двох гілок є їх побічні ефекти, я б, мабуть, використав ifтвердження.

Ось зразок програми та байт-код:

public class Test {
    public static void main(String[] args) {
        int x;
        if (args.length > 0) {
            x = 1;
        } else {
            x = 2;
        }
    }

    public static void main2(String[] args) {
        int x = (args.length > 0) ? 1 : 2;
    }
}

Байт-код декомпілюється за допомогою javap -c Test:

public class Test extends java.lang.Object {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1
       4: return

  public static void main(java.lang.String[]
    Code:
       0: aload_0
       1: arraylength
       2: ifle          10
       5: iconst_1
       6: istore_1
       7: goto          12
      10: iconst_2
      11: istore_1
      12: return

  public static void main2(java.lang.String[
    Code:
       0: aload_0
       1: arraylength
       2: ifle          9
       5: iconst_1
       6: goto          10
       9: iconst_2
      10: istore_1
      11: return
}

Як бачите, тут є невелика різниця в байт-коді - istore_1відбувається це всередині фігурного дужка чи ні (на відміну від моєї попередньої надзвичайно помилкової спроби :), але я був би дуже здивований, якби JITter опинився з іншим нативним кодом.


s / умовна заява / умовний вираз /
Лоуренс Гонсалвес,

1
Я здогадуюсь, ти не мав на увазі обох mainі main2бути абсолютно однаковими?
ColinD

вражаюче. Я не знав, що ти міг скомпілювати байт-код до цього часу.
Kyle

2
@Kyle: Я скомпілював Java, а потім декомпілював за допомогою javap.
Джон Скіт,

1
@Kyle: Саме так. Я здебільшого очікував, що байт-код буде однаковим . Так воно і є, майже майже однакове :)
Джон Скіт,

10

Обидва ваші приклади, ймовірно, будуть скомпільовані в однаковий або майже ідентичний байт-код, тому різниці в продуктивності не повинно бути.

Якби була різниця у швидкості виконання, ви все одно мали б використовувати найідіомічнішу версію (яка буде другою для призначення однієї змінної на основі простої умови та двох простих підвиразів, а перша - для виконання більш складних операцій або операції, які не вміщуються в одному рядку).


8

Це те саме. Обидва вони досить швидкі, як правило, близько 10-30 наносекунд. (залежно від режиму використання) Чи важливий для вас цей часовий проміжок?

Ви повинні робити те, що, на вашу думку, є найбільш зрозумілим.


4

Просто додати до всіх інших відповідей:

Другий вираз часто називають третинним / потрійним оператором / твердженням. Це може бути дуже корисним, оскільки повертає вираз. Іноді це робить код більш зрозумілим для типових коротких висловлювань.


4
Чудовий приклад цього на практиці: у Java, якщо мені потрібно зробити String final на основі результату виразу, я можу використовувати кінцевий синтаксис final String whichTable = (Integer.parseInt (clientId)> 500)? "serverClients": "offlineClients"; Тоді я можу використовувати значення виразу в місцях, де whichTable має бути остаточним. Наступне буде незаконним: final String whichTable = ""; якщо (Integer.parseInt (clientId)> 500) {whichTable = "serverClients"; } else {whichTable = "offlineClients"; }
James Perih

@JamesPerih У випадку finalполя ви можете використовувати блоки конструкторів для встановлення значення (хоча умовний оператор виглядає у IMO у мільярд разів кращим), а за допомогою локальних змінних ви можете призначити значення перед першим використанням пізніше в блоці коду, який ви Я вхожу. Я думаю, що єдиний випадок, коли тернар має перевагу над if-elseтим, коли викликає конструктор super(...)або this(...)всередині нього.
Кров


0

Тернарний оператор швидший, ніж умова if-else.

public class TerinaryTest {
    public static void main(String[] args)
    {
        int j = 2,i = 0;
        Date d1 = new Date();
        for(long l=1;l<100000000;l++)
            if(i==1) j=1;
                else j=0;
        Date d2 = new Date();
        for(long l=1;l<100000000;l++)
            j=i==1?1:0;
        Date d3 = new Date();
        System.out.println("Time for if-else: " + (d2.getTime()-d1.getTime()));
        System.out.println("Time for ternary: " + (d3.getTime()-d2.getTime()));
    }
}

Результати тесту:

Доріжка-1:

Час на інше: 63

Час для потрійного: 31

Стежка-2:

Час на інше: 78

Час для потрійного: 47

Стежка-3:

Час на інше: 94

Час для потрійного: 31

Стежка-4:

Час на інше: 78

Час для потрійного: 47


У мене був абсолютно протилежний результат при запуску вашого прикладу, який показує, що результати ненадійні. На жаль, ви потрапляєте в пастку мікровиробничого маркування - як відомо, важко правильно зробити мікрознаки. Ось кілька прикладів, які ви можете побачити тут: stackoverflow.com/questions/2842695/what-is-microbenchmarking
Рогач,

Ваш конкретний приклад страждає від принаймні цих проблем: 4 випробувань нікуди не вистачає, ви запускаєте тести завжди в однаковому порядку (перший якщо-інший, другий потрійний), ви не прогріваєте JVM перед запуском тестів тощо.
Рогач
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.