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