Какая конструкция «if» быстрее - оператор или тернарный оператор?

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
Оба будут возбуждены. Никакой разницы не будет. И не беспокойтесь о декомпиляции. Первое, что делает HotSpot, это удаляет все оптимизации, примененные javac.
Иво Ветцель
11
Их не существует для разных скоростей. Они существуют для разных целей. Я уверен, что вы понимаете разницу между утверждениями и выражениями. Заявления выполняют действия. Выражения производят ценности. ifиспользуется в заявлениях. ?используется в выражениях.
Майк Данлэйви
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
впечатляет. Я не знал, что вы можете компилировать байтовый код до сих пор.
Кайл
2
@Kyle: Я скомпилировал Java, а затем декомпилировал с помощью javap.
Джон Скит
1
@ Кайл: Совершенно верно. В основном я ожидал, что байт-код будет идентичным . Как бы то ни было, он почти идентичен :)
Джон Скит
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"; }
Джеймс Перих
@JamesPerih В случае finalполя вы можете использовать блоки конструктора для установки значения (хотя условный оператор выглядит в миллиард раз лучше IMO), а с локальными переменными вы можете присвоить значение перед первым использованием позже в блоке кода, который вы Я думаю, единственный случай, когда тройка даст преимущество, if-else- это вызов super(...)или this(...)внутри конструктора.
Kröw
3

ни то, ни другое - они будут скомпилированы так же.

Фредди
источник
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:

Время для if-else: 63

Время для троичного: 31

Тропа-2:

Время для if-else: 78

Время для троичного: 47

Тропа-3:

Время для if-else: 94

Время для троичного: 31

Тропа-4:

Время для if-else: 78

Время для троичного: 47

rmkyjv
источник
При запуске вашего примера у меня были прямо противоположные результаты, которые показывают, что результаты ненадежны. К сожалению, вы попадаете в ловушку микробенчмарков - как известно, правильно делать микробенчмарки. Пару примеров вы можете увидеть здесь: stackoverflow.com/questions/2842695/what-is-microbenchmarking
Rogach
В вашем конкретном примере есть как минимум следующие проблемы: 4 испытаний недостаточно, вы запускаете тесты всегда в одном и том же порядке (первый if-else, второй тройной), вы не прогреваете JVM перед запуском тестов и т. Д.
Рогач