Why is using String in a switch block more efficient than in an if-else block?

13

According to Java documentation : / p>

  

The Java compiler generally generates more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.

    

asked by anonymous 26.08.2015 / 16:37

1 answer

14

The main reason in this case is that it does not compare the strings with equals() and yes with hashCode() . After compiling each case will save the hash of the string and not the string itself. It then generates the hash of the variable being used in switch and compares these integer values which is much faster than comparing a string.

This allows for a gain in search because switch uses a lookup table instead of going through each condition. The if is O (N) and the switch is usually O (1).

Bytecode compared ( removed from this answer in SO ):

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
}

Although the code is larger, it runs faster because it does not have to go through all of it like the previous one.

Note that there is a comparison of string to confirm if there was no hash collision but it is executed only for the case that hashed hash equal and not for all conditions.

Obviously the difference is most noticeable when there are several case s. With few, there may even be loss of performance. Of course this will also depend on the order of what needs to be found. If the first if satisfies the condition is probably faster than switch (I can not guarantee because it depends on implementation).

Another thing that can make if faster is if the string is too short. A direct comparison turns out to be faster than having to hash and then comparing your result.

    
26.08.2015 / 16:54