The main reason in this case is that it does not compare the strings with equals() rather with hashCode(). After each compiled case will guard the hash of string and not the string in itself. There it generates the hash of the variable being used in the switch and compare these integer values which is much faster than comparing a string of characters (if you have any form of cache of hash code, otherwise it could be worse).
Thus allows there to be a gain in the search because the switch uses a table of lookup instead of going through each condition. The if is O(n) and switch is usually O(1).
Bytecode compared (withdrawn from that reply in the OS):
Compiled from "CompileSwitch.java"
public class CompileSwitch {
public CompileSwitch();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String C
2: astore_1
3: ldc #18 // String A
5: aload_1
6: invokevirtual #20 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
9: ifne 28
12: ldc #26 // String B
14: aload_1
15: invokevirtual #20 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
18: ifne 28
21: ldc #16 // String C
23: aload_1
24: invokevirtual #20 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
27: pop
28: return
}
###Switch
Compiled from "CompileSwitch.java"
public class CompileSwitch {
public CompileSwitch();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String C
2: astore_1
3: aload_1
4: dup
5: astore_2
6: invokevirtual #18 // Method java/lang/String.hashCode:()I
9: lookupswitch { // 3
65: 44
66: 56
67: 68
default: 77
}
44: aload_2
45: ldc #24 // String A
47: invokevirtual #26 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
50: ifne 77
53: goto 77
56: aload_2
57: ldc #30 // String B
59: invokevirtual #26 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
62: ifne 77
65: goto 77
68: aload_2
69: ldc #16 // String C
71: invokevirtual #26 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
74: ifne 77
77: return
}
Even though the code is bigger, it runs faster by not having to go through it all like in the previous.
Note that there is a comparison of string to confirm that there was no collision of hash but she is executed only for the case she gave hash equal and not for all conditions.
Obviously the difference becomes more noticeable when there are several cases. With few there may even be loss of performance. And of course this will also depend on the order of what needs to be found. If the first if satisfy the condition is probably faster than the switch (do not guarantee because depends on implementation).
Another reason that can do the if be faster if the string is very short. A direct comparison ends up being faster than having to generate the hash and then compare your result.