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

Asked

Viewed 276 times

14

According to the java documentation:

The Java Compiler generates generally more Efficient bytecode from switch statements that use String Objects than from chained if-then-Else statements.

Not to mention the fact that use .equals() makes case-sensitive, how precisely this code is most "efficient"?

1 answer

15


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.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.