DATE: <$BlogItemDate$> 4/27/2007 03:24:00 PM ----- TITLE:Switches BODY:
As a vague effort to keep this site on the fringe of 'maintained', here is a rambling from the world of Java ME development. How Switch Statements Work in Java Switch statements are more efficient than if-else chains in all good languages and java is no exception. It's worth understanding how they work though when considering a piece of code that must run as small and as fast as possible. Consider the following example:
switch(a)
{
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
break;
case 3:
System.out.println("3");
break;
case 4:
System.out.println("4");
break;
case 5:
System.out.println("5");
break;
}
return;
We can see what bytecode this produces by using the javap command
Code:
0: iload_1
1: tableswitch{ //1 to 5
1: 36;
2: 47;
3: 58;
4: 69;
5: 80;
default: 88 }
36: getstatic #16;
39: ldc #22;
41: invokevirtual #24;
44: goto 88
47: getstatic #16;
50: ldc #30;
52: invokevirtual #24;
55: goto 88
58: getstatic #16;
61: ldc #32;
63: invokevirtual #24;
66: goto 88
69: getstatic #16;
72: ldc #34;
74: invokevirtual #24;
77: goto 88
80: getstatic #16;
83: ldc #36;
85: invokevirtual #24;
88: return
The switch statement produces a 'tableswitch' command which is literally a table of values, indexed by key integers (The case values). By reading the table at the position indicated by the case value, you can find out the position of the next instruction.
Let's see what happens when we change the final 'case 5' line to a 'case 6':
tableswitch{ //1 to 6
1: 40;
2: 51;
3: 62;
4: 73;
5: 92;
6: 84;
default: 92 }
The table now contains 6 entries instead of 5, despite the fact that we no longer have a case statement for 5. Looking at the entry for '5' we see that a value of 5 takes us directly out of the switch block to our return statement.
We would also notice an increase in the size of our class file. The dummy entry costs us 4 bytes onto the size of our class file.
Let's try changing the 6 now to a 12:
tableswitch{ //1 to 12
1: 64;
2: 75;
3: 86;
4: 97;
5: 116;
6: 116;
7: 116;
8: 116;
9: 116;
10: 116;
11: 116;
12: 108;
default: 116 }
As you may have expected, the table just got a whole lot larger, and the class file jumped to 28 bytes larger than our first example with no apparent change in functionality. You can see entries for the numbers 5 to 11 plugging the 'gap' in the range of values for the switch statement.
Let's push it a little farther and try changing the 12 to a 13:
lookupswitch{ //5
1: 52;
2: 63;
3: 74;
4: 85;
13: 96;
default: 104 }
Now something different has happened. The class file is still larger than it might have been (by 16 bytes, to be exact) but the table has shrunk back down to 5 entries.
The instruction though, is now different. Instead of a table, the JVM will recieve a 'lookupswitch' instruction. This instruction does not use an efficient table lookup to find the next instruction. Instead, it provides a sorted list of case values and instruction pointers.
At run-time, the JVM/KVM implementation is free to use whatever means to use this information to determine the next instruction. The may mean a binary search, or it may mean a linear search through the lookup list.
The compiler will substitute a table switch for a lookup switch when it judges that the table method is becoming too costly in terms of space.
Switch statement strategy
For the most part, this doesn't really matter very much. It may matter though, if you are implementing code that simply must be fast, and must be small. In this case you may wish to take notice of the switch values you are using and if possible, alter them.
To make the most efficient use of switch values you should use contiguous values, preferably with as few gaps (Or no gaps) in the range as possible.
--------