Java: if vs. Switch

I have a piece of code with a) that I replaced with b) purely for readability ...

but)

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


... will the cascading version switch through all permutations or move on to the case?



EDIT:

Some of the answers below discuss alternative approaches to the above approach.
I have included the following to provide context for its use.

The reason I asked, the question above, was because the speed of adding words empirically improved.

This is not production code, and was quickly cracked like PoC.

The following seems to confirm failure for a thought experiment.
I may need a much larger body of words than the one I am currently using. The error occurs due to the fact that I did not take into account null references that still require 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 ); } } 
+16
java comparison switch-statement if-statement
Jun 29 '09 at 23:39
source share
11 answers

There are two forms of switch in the tableswitch : tableswitch and lookupswitch . One involves a tight set of keys, the other is sparse. See the description of the compiler in the JVM specification . For enumerations, a sequence number is found, and then the code continues as an int case. I'm not quite sure how the small switch on String function in JDK7 will be implemented.

However, heavily used code is usually compiled in any reasonable JVM. The optimizer is not completely stupid. Do not worry about this and follow the usual heuristics for optimization.

+20
Jun 29 '09 at 23:49
source share

Do not worry about performance; use the syntax that best expresses what you are doing. Only after you (a) have demonstrated a lack of performance; and (b) localize it to the routine in question, only then you need to worry about performance. The case syntax is more suitable for my money here.

+23
Jun 29 '09 at 23:46
source share

Looks like you listed the values, so maybe the listing is ok?

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

Then in your code:

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

In addition, there is a possible error in your code, where "A" == "A" may be false depending on the identifier of the object "A".

+7
Jun 30 '09 at 3:29
source share

Not quite the answer to your question, but actually there is an error in your code, you should have a gap after each case:

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

I donโ€™t think that the differences in performance here will be too significant, but if you really care about performance, and if this code is executed very often, here is another option:

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

Be sure to encapsulate this and document it well.

+4
Jun 29 '09 at 23:57
source share

Honestly, I do not think that performance is important in this case. This is valid until the compiler and its optimization.

+3
Jun 29 '09 at 23:47
source share

If you have a switch statement with sequential integer values, depending on the language, it can be optimized for the branch table, which is very fast. Anyway, this is not slower!

In addition, using if / else if will be an improvement if, for example, for cases where your affairs are mutually exclusive. It makes no sense to do another 25 checks after matching A.

But in principle, any performance difference is negligible, and you should use the most correct syntax, which in this case is the switch statement. Remember to share your business intermittently.

+3
Jun 29 '09 at 23:48
source share

All case statements can be avoided here.

 import java.util.HashMap; public class Dictionary { private static Dictionary ROOT; private boolean terminus; private final HashMap<Character, Dictionary> dictionaries = new HashMap<Character, Dictionary>(); 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); } } 
+3
Jun 30 '09 at 17:31
source share

I know that this is not what you are asking for, but are you not doing it?

 public class Dictionary { private static final Set<String> WORDS = new HashSet<String>(); 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()); } } 

Are you just looking for something more memory efficient?

+2
Jul 02 '09 at 15:55
source share

The switch should use a hash to choose which case to address. From there, each subsequent case will also be launched if there are no break statements. For example, with your code, if you include X, it will immediately go to X, then Y, then Z. See the Java Tutorial .

+1
Jun 29 '09 at 23:46
source share

switch should be logarithmic, and if - linear, if the compiler cannot find anything clever. But long switch es are difficult to read, and also prone to errors - as already noted, the switch that you have above has no gaps, and it will go through all the cases.

Why not get Map instead, but just use Map.get() ?

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

As noted above, if BRANCH is an Enum , this behavior should be properly in Enum .

(What is WORD , INDEX and BRANCH here, anyway? Of the names, they must be constants, but you cannot have constant arrays - the contents can also be changed, there is a lot of sense in creating a constant "structure", and, of course, not it would make much sense iffing or switching based on constants ....)

+1
Jun 30 '09 at 8:52
source share

I think this is more a matter of style, not performance. I think that in this case the switch statement is more appropriate than if. I'm not sure there is a big difference in performance.

0
Jun 30 '09 at 2:09
source share



All Articles