Java: se vs.

Eu tenho um pedaço de código com a) que eu substituí com b) apenas para legibilidade …

a)

if ( WORD[ INDEX ] == 'A' ) branch = BRANCH.A; /* B through to Y */ if ( WORD[ INDEX ] == 'Z' ) branch = BRANCH.Z; 

b)

 switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A; break; /* B through to Y */ case 'Z' : branch = BRANCH.Z; break; } 

… a versão do switch vai passar por todas as permutações ou pular para um case?

EDITAR:

Algumas das respostas abaixo consideram abordagens alternativas para a abordagem acima.
Eu incluí o seguinte para fornecer contexto para seu uso.

A razão que eu perguntei, a pergunta acima, foi porque a velocidade de adicionar palavras melhorou empiricamente.

Este não é um código de produção e foi cortado rapidamente como um PoC.

O seguinte parece ser uma confirmação do fracasso de um experimento mental.
Eu posso precisar de um corpus de palavras muito maior do que o que estou usando atualmente.
A falha surge do fato de eu não considerar as referências nulas que ainda exigem memory. (doh!)

 public class Dictionary { private static Dictionary ROOT; private boolean terminus; private Dictionary A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z; private static Dictionary instantiate( final Dictionary DICTIONARY ) { return ( DICTIONARY == null ) ? new Dictionary() : DICTIONARY; } private Dictionary() { this.terminus = false; this.A = this.B = this.C = this.D = this.E = this.F = this.G = this.H = this.I = this.J = this.K = this.L = this.M = this.N = this.O = this.P = this.Q = this.R = this.S = this.T = this.U = this.V = this.W = this.X = this.Y = this.Z = null; } public static void add( final String...STRINGS ) { Dictionary.ROOT = Dictionary.instantiate( Dictionary.ROOT ); for ( final String STRING : STRINGS ) Dictionary.add( STRING.toUpperCase().toCharArray(), Dictionary.ROOT , 0, STRING.length() - 1 ); } private static void add( final char[] WORD, final Dictionary BRANCH, final int INDEX, final int INDEX_LIMIT ) { Dictionary branch = null; switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A = Dictionary.instantiate( BRANCH.A ); break; case 'B' : branch = BRANCH.B = Dictionary.instantiate( BRANCH.B ); break; case 'C' : branch = BRANCH.C = Dictionary.instantiate( BRANCH.C ); break; case 'D' : branch = BRANCH.D = Dictionary.instantiate( BRANCH.D ); break; case 'E' : branch = BRANCH.E = Dictionary.instantiate( BRANCH.E ); break; case 'F' : branch = BRANCH.F = Dictionary.instantiate( BRANCH.F ); break; case 'G' : branch = BRANCH.G = Dictionary.instantiate( BRANCH.G ); break; case 'H' : branch = BRANCH.H = Dictionary.instantiate( BRANCH.H ); break; case 'I' : branch = BRANCH.I = Dictionary.instantiate( BRANCH.I ); break; case 'J' : branch = BRANCH.J = Dictionary.instantiate( BRANCH.J ); break; case 'K' : branch = BRANCH.K = Dictionary.instantiate( BRANCH.K ); break; case 'L' : branch = BRANCH.L = Dictionary.instantiate( BRANCH.L ); break; case 'M' : branch = BRANCH.M = Dictionary.instantiate( BRANCH.M ); break; case 'N' : branch = BRANCH.N = Dictionary.instantiate( BRANCH.N ); break; case 'O' : branch = BRANCH.O = Dictionary.instantiate( BRANCH.O ); break; case 'P' : branch = BRANCH.P = Dictionary.instantiate( BRANCH.P ); break; case 'Q' : branch = BRANCH.Q = Dictionary.instantiate( BRANCH.Q ); break; case 'R' : branch = BRANCH.R = Dictionary.instantiate( BRANCH.R ); break; case 'S' : branch = BRANCH.S = Dictionary.instantiate( BRANCH.S ); break; case 'T' : branch = BRANCH.T = Dictionary.instantiate( BRANCH.T ); break; case 'U' : branch = BRANCH.U = Dictionary.instantiate( BRANCH.U ); break; case 'V' : branch = BRANCH.V = Dictionary.instantiate( BRANCH.V ); break; case 'W' : branch = BRANCH.W = Dictionary.instantiate( BRANCH.W ); break; case 'X' : branch = BRANCH.X = Dictionary.instantiate( BRANCH.X ); break; case 'Y' : branch = BRANCH.Y = Dictionary.instantiate( BRANCH.Y ); break; case 'Z' : branch = BRANCH.Z = Dictionary.instantiate( BRANCH.Z ); break; } if ( INDEX == INDEX_LIMIT ) branch.terminus = true; else Dictionary.add( WORD, branch, INDEX + 1, INDEX_LIMIT ); } public static boolean is( final String STRING ) { Dictionary.ROOT = Dictionary.instantiate( Dictionary.ROOT ); return Dictionary.is( STRING.toUpperCase().toCharArray(), Dictionary.ROOT, 0, STRING.length() - 1 ); } private static boolean is( final char[] WORD, final Dictionary BRANCH, final int INDEX, final int INDEX_LIMIT ) { Dictionary branch = null; switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A; break; case 'B' : branch = BRANCH.B; break; case 'C' : branch = BRANCH.C; break; case 'D' : branch = BRANCH.D; break; case 'E' : branch = BRANCH.E; break; case 'F' : branch = BRANCH.F; break; case 'G' : branch = BRANCH.G; break; case 'H' : branch = BRANCH.H; break; case 'I' : branch = BRANCH.I; break; case 'J' : branch = BRANCH.J; break; case 'K' : branch = BRANCH.K; break; case 'L' : branch = BRANCH.L; break; case 'M' : branch = BRANCH.M; break; case 'N' : branch = BRANCH.N; break; case 'O' : branch = BRANCH.O; break; case 'P' : branch = BRANCH.P; break; case 'Q' : branch = BRANCH.Q; break; case 'R' : branch = BRANCH.R; break; case 'S' : branch = BRANCH.S; break; case 'T' : branch = BRANCH.T; break; case 'U' : branch = BRANCH.U; break; case 'V' : branch = BRANCH.V; break; case 'W' : branch = BRANCH.W; break; case 'X' : branch = BRANCH.X; break; case 'Y' : branch = BRANCH.Y; break; case 'Z' : branch = BRANCH.Z; break; } if ( branch == null ) return false; if ( INDEX == INDEX_LIMIT ) return branch.terminus; else return Dictionary.is( WORD, branch, INDEX + 1, INDEX_LIMIT ); } } 

No bytecode existem duas formas de switch: a comutador de tableswitch e a chave de tableswitch . Um assume um denso conjunto de chaves, o outro escasso. Veja a descrição da opção de compilation na especificação da JVM . Para enums, o ordinal é encontrado e, em seguida, o código continua como o caso int . Eu não estou completamente certo de como a switch proposta no pequeno recurso String no JDK7 será implementada.

No entanto, o código muito usado é normalmente compilado em qualquer JVM sensata. O otimizador não é totalmente estúpido. Não se preocupe com isso e siga as heurísticas usuais para otimização.

Não se preocupe com desempenho; use a syntax que melhor expresse o que você está fazendo. Somente depois de ter (a) demonstrado uma deficiência de desempenho; e (b) localizado na rotina em questão, só então você deve se preocupar com o desempenho. Pelo meu dinheiro, a syntax do caso é mais apropriada aqui.

Parece que você enumerou os valores, então talvez uma enumeração esteja em ordem?

 enum BRANCH { A,B, ... Y,Z; } 

Então, no seu código:

 BRANCH branch = BRANCH.valueOf( WORD[ INDEX ] ); 

Além disso, há um possível bug no seu código, onde "A" == "A" pode ser falso dependendo da identidade do object do “A”.

Não é bem uma resposta à sua pergunta, mas há um bug no seu código, você deve ter uma pausa após cada caso:

 switch ( WORD[ INDEX ] ) { case 'A' : branch = BRANCH.A; break; /* B through to Y */ case 'Z' : branch = BRANCH.Z; break; } 

Eu não acho que as diferenças de desempenho serão muito significativas aqui, mas se você realmente se importa com o desempenho, e se este código é executado com muita frequência, aqui está outra opção:

 // Warning, untested code. BRANCH[] branchLookUp = {BRANCH.A, BRANCH.B, ..., BRANCH.Z}; branch = branchLookUp[WORD[INDEX] - 'A']; 

Certifique-se de encapsular isso e documentá-lo bem.

Honestamente, não acho que o desempenho seja importante neste caso. Cabe ao compilador e sua otimização.

Se você tiver uma instrução switch com valores integrais consecutivos, dependendo do idioma, ela poderá ser otimizada para uma tabela de ramificação, o que é muito rápido. Não é mais lento, de qualquer maneira!

Além disso, usar if / else seria uma melhoria em relação a if, para casos como este em que seus casos são mutuamente exclusivos. Não faz sentido fazer mais 25 verificações após a correspondência A.

Mas, basicamente, qualquer diferença de desempenho é insignificante e você deve usar a syntax mais correta, que nesse caso é a instrução switch. Certifique-se de separar seus casos com quebras embora.

Aqui está uma maneira de evitar todas as declarações de caso.

 import java.util.HashMap; public class Dictionary { private static Dictionary ROOT; private boolean terminus; private final HashMap dictionaries = new HashMap(); private void ensureBranch(char c) { if (getBranch(c) != null) return; dictionaries.put(c, new Dictionary()); } private Dictionary getBranch(char c) { return dictionaries.get(c); } public static boolean is(final String string) { ensureRoot(); return is(chars(string), ROOT, 0, string.length() - 1); } public static void add(final String... strings) { ensureRoot(); for (final String string : strings) add(chars(string), ROOT, 0, string.length() - 1); } private static void ensureRoot() { if (ROOT == null) ROOT = new Dictionary(); } private static char[] chars(final String string) { return string.toUpperCase().toCharArray(); } private Dictionary() { this.terminus = false; } private static void add(final char[] word, final Dictionary dictionary, final int index, final int limit) { Dictionary branch = getBranch(word, dictionary, index); if (index == limit) branch.terminus = true; else add(word, branch, index + 1, limit); } private static Dictionary getBranch(final char[] word, final Dictionary dictionary, final int index) { final char c = word[index]; dictionary.ensureBranch(c); return dictionary.getBranch(c); } private static boolean is(final char[] word, final Dictionary dictionary, final int index, final int limit) { Dictionary branch = dictionary.getBranch(word[index]); if (branch == null) return false; if (index == limit) return branch.terminus; return is(word, branch, index + 1, limit); } } 

Eu sei que isso não é o que você está perguntando, mas você não está apenas fazendo isso?

 public class Dictionary { private static final Set WORDS = new HashSet(); public static void add(final String... STRINGS) { for (String str : STRINGS) { WORDS.add(str.toUpperCase()); } } public static boolean is(final String STRING) { return WORDS.contains(STRING.toUpperCase()); } } 

Você está simplesmente procurando algo um pouco mais eficiente de memory?

A instrução switch deve usar um hash para selecionar para qual caso ir. A partir daí, todos os casos subsequentes também serão executados se não houver instruções de break . Por exemplo, com seu código, se você ativar X, ele irá imediatamente para X, Y e Z. Veja o Java Tutorial .

O switch deve ser logarítmico e o if linear, supondo que o compilador não encontre nada inteligente. Mas os switch longos são complicados de ler e também propensos a erros – como notado, o switch acima não tem interrupções, e vai cair em todos os casos.

Por que não preencher um Map vez disso e apenas usar Map.get() ?

 private static final Map BRANCHES = Collections.unmodifiableMap(new HashMap() {{ put('A', BRANCH.A); ... put('Z', BRANCH.Z); }} public void getBranch(char[] WORD, int INDEX) { return BRANCHES.get(WORD[INDEX]); } 

Conforme observado acima, se BRANCH for um Enum , esse comportamento deve estar corretamente no Enum .

(O que são WORD , INDEX e BRANCH aqui, afinal? Dos nomes, eles devem ser constantes, mas você não pode ter arrays constantes – os conteúdos são sempre modificáveis; não haveria muito sentido em criar uma constante ” struct “; e certamente não haveria muito sentido em iffing ou switching baseado em constantes ….)

Eu acho que isso é mais uma questão sobre estilo ao invés de performance. Eu acho que neste caso a instrução switch é mais apropriada do que se fosse. Não tenho certeza se há muita diferença no desempenho.