Qual construção “se” é mais rápida - instrução ou operador ternário?

83

Existem dois tipos de ifdeclarações em java - clássico: if {} else {}e taquigrafia: exp ? value1 : value2. Um é mais rápido do que o outro ou são iguais?

declaração:

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

operador ternário:

int x = (expression) ? 1 : 2;
Rogach
fonte
34
Acho que não há absolutamente nenhuma diferença. É apenas sintaxe. A menos que os compiladores sejam um tanto maus (ou algo mais) e eu esteja errado
sinelaw
4
Você (micro) comparou isso? Compartilhe os resultados.
BalusC de
3
Ambos ficarão jit'ed. Não haverá diferença alguma. E não se preocupe em descompilar as coisas. A primeira coisa que o HotSpot faz é remover todas as otimizações que foram aplicadas pelo javac.
Ivo Wetzel
11
Eles não existem para velocidades diferentes. Eles existem para finalidades diferentes. Tenho certeza que você entende a diferença entre declarações e expressões. As instruções executam ações. Expressões produzem valores. ifé para uso em declarações. ?é para uso em expressões.
Mike Dunlavey
3
+1, pois vale a pena ler as respostas a esta pergunta, mesmo que a intenção da pergunta original seja mal orientada.
jball de

Respostas:

106

Existe apenas um tipo de declaração "if" ali. A outra é uma expressão condicional. Quanto a qual terá melhor desempenho: eles podem compilar para o mesmo bytecode, e eu esperaria que eles se comportassem de forma idêntica - ou tão perto que você definitivamente não gostaria de escolher um em relação ao outro em termos de desempenho.

Às vezes, uma ifinstrução será mais legível, às vezes o operador condicional será mais legível. Em particular, eu recomendaria usar o operador condicional quando os dois operandos são simples e livres de efeitos colaterais, ao passo que, se o objetivo principal dos dois ramos forem seus efeitos colaterais, provavelmente usaria uma ifinstrução.

Aqui está um programa de amostra e bytecode:

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;
    }
}

Bytecode descompilado com 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
}

Como você pode ver, há uma ligeira diferença no bytecode aqui - se ele istore_1ocorre dentro do brance ou não (ao contrário da minha tentativa anterior extremamente falha :) mas eu ficaria muito surpreso se o JITter acabasse com um código nativo diferente.

Jon Skeet
fonte
s / declaração condicional / expressão condicional /
Laurence Gonsalves
1
Eu estou supondo que você não quis para ambos maine main2para ser exatamente o mesmo?
ColinD de
impressionante. Eu não sabia que você poderia compilar código de bytes até agora.
Kyle
2
@Kyle: Compilei o Java e depois descompilei com javap.
Jon Skeet
1
@Kyle: Exatamente. Eu esperava que o bytecode fosse idêntico . Como está, é quase idêntico :)
Jon Skeet
10

Ambos os exemplos provavelmente serão compilados para bytecode idêntico ou quase idêntico, portanto, não deve haver diferença no desempenho.

Se houvesse uma diferença na velocidade de execução, você ainda deveria usar a versão mais idiomática (que seria a segunda para atribuir uma única variável com base em uma condição simples e duas subexpressões simples, e a primeira para fazer operações mais complexas ou operações que não cabem em uma única linha).

Victor Nicollet
fonte
8

São iguais. Ambos são bastante rápidos, normalmente em torno de 10-30 nanossegundos. (dependendo do padrão de uso) Este período de tempo é importante para você?

Você deve fazer o que acredita ser mais claro.

Peter Lawrey
fonte
4

Apenas para adicionar a todas as outras respostas:

A segunda expressão é freqüentemente chamada de operador / instrução terciário / ternário. Pode ser muito útil porque retorna uma expressão. Às vezes, torna o código mais claro para instruções curtas típicas.

Secko
fonte
4
Grande exemplo disso na prática: em Java, se eu tiver que fazer uma String final com base no resultado de uma expressão, posso usar a sintaxe ternária final String whichTable = (Integer.parseInt (clientId)> 500)? "serverClients": "offlineClients"; Então, posso usar o valor da expressão em locais em que a tabela precisa ser final. O seguinte seria ilegal: final String whichTable = ""; if (Integer.parseInt (clientId)> 500) {whichTable = "serverClients"; } else {whichTable = "offlineClients"; }
James Perih
@JamesPerih No caso de um finalcampo, você pode usar blocos construtores para definir um valor (embora o operador condicional pareça um bilhão de vezes melhor IMO), e com variáveis ​​locais, você pode atribuir um valor antes de usar pela primeira vez mais tarde no bloco de código que você está dentro. Acho que o único caso em que um ternário daria uma vantagem sobre if-elseé ao chamar super(...)ou this(...)dentro de um construtor.
Kröw
3

nenhum - eles serão compilados da mesma forma.

Freddie
fonte
0

O operador ternário é mais rápido do que a condição 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()));
    }
}

Resultado dos testes:

Trilha-1:

Tempo para if-else: 63

Tempo para o ternário: 31

Trilha-2:

Tempo para if-else: 78

Tempo para o ternário: 47

Trail-3:

Tempo para if-else: 94

Tempo para o ternário: 31

Trail-4:

Tempo para if-else: 78

Tempo para o ternário: 47

rmkyjv
fonte
Tive resultados exatamente opostos ao executar seu exemplo, o que mostra que os resultados não são confiáveis. Infelizmente, você está caindo na armadilha do microbenchmarking - é notoriamente difícil fazer os microbenchmarks corretamente. Para alguns exemplos, você pode ver aqui: stackoverflow.com/questions/2842695/what-is-microbenchmarking
Rogach
Seu exemplo particular sofre de pelo menos estes problemas: 4 tentativas não é suficiente, você executa os testes sempre na mesma ordem (primeiro if-else, segundo ternário), você não aquece a JVM antes de executar os testes, etc.
Rogach