What is the relative performance difference of if/else versus switch statement in Java?

前端 未结 8 1204
北恋
北恋 2020-11-22 16:25

Worrying about my web application\'s performances, I am wondering which of \"if/else\" or switch statement is better regarding performance?

相关标签:
8条回答
  • 2020-11-22 16:42

    In my test the better performance is ENUM > MAP > SWITCH > IF/ELSE IF in Windows7.

    import java.util.HashMap;
    import java.util.Map;
    
    public class StringsInSwitch {
    public static void main(String[] args) {
        String doSomething = null;
    
    
        //METHOD_1 : SWITCH
        long start = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            String input = "Hello World" + (i & 0xF);
    
            switch (input) {
            case "Hello World0":
                doSomething = "Hello World0";
                break;
            case "Hello World1":
                doSomething = "Hello World0";
                break;
            case "Hello World2":
                doSomething = "Hello World0";
                break;
            case "Hello World3":
                doSomething = "Hello World0";
                break;
            case "Hello World4":
                doSomething = "Hello World0";
                break;
            case "Hello World5":
                doSomething = "Hello World0";
                break;
            case "Hello World6":
                doSomething = "Hello World0";
                break;
            case "Hello World7":
                doSomething = "Hello World0";
                break;
            case "Hello World8":
                doSomething = "Hello World0";
                break;
            case "Hello World9":
                doSomething = "Hello World0";
                break;
            case "Hello World10":
                doSomething = "Hello World0";
                break;
            case "Hello World11":
                doSomething = "Hello World0";
                break;
            case "Hello World12":
                doSomething = "Hello World0";
                break;
            case "Hello World13":
                doSomething = "Hello World0";
                break;
            case "Hello World14":
                doSomething = "Hello World0";
                break;
            case "Hello World15":
                doSomething = "Hello World0";
                break;
            }
        }
    
        System.out.println("Time taken for String in Switch :"+ (System.currentTimeMillis() - start));
    
    
    
    
        //METHOD_2 : IF/ELSE IF
        start = System.currentTimeMillis();
    
        for (int i = 0; i < 99999999; i++) {
            String input = "Hello World" + (i & 0xF);
    
            if(input.equals("Hello World0")){
                doSomething = "Hello World0";
            } else if(input.equals("Hello World1")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World2")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World3")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World4")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World5")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World6")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World7")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World8")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World9")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World10")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World11")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World12")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World13")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World14")){
                doSomething = "Hello World0";
    
            } else if(input.equals("Hello World15")){
                doSomething = "Hello World0";
    
            }
        }
        System.out.println("Time taken for String in if/else if :"+ (System.currentTimeMillis() - start));
    
    
    
    
    
    
    
    
    
        //METHOD_3 : MAP
        //Create and build Map
        Map<String, ExecutableClass> map = new HashMap<String, ExecutableClass>();
        for (int i = 0; i <= 15; i++) {
            String input = "Hello World" + (i & 0xF);
            map.put(input, new ExecutableClass(){
                                public void execute(String doSomething){
                                    doSomething = "Hello World0";
                                }
                            });
        }
    
    
        //Start test map
        start = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            String input = "Hello World" + (i & 0xF);
            map.get(input).execute(doSomething);
        }
        System.out.println("Time taken for String in Map :"+ (System.currentTimeMillis() - start));
    
    
    
    
    
    
        //METHOD_4 : ENUM (This doesn't use muliple string with space.)
        start = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            String input = "HW" + (i & 0xF);
            HelloWorld.valueOf(input).execute(doSomething);
        }
        System.out.println("Time taken for String in ENUM :"+ (System.currentTimeMillis() - start));
    
    
        }
    
    }
    
    interface ExecutableClass
    {
        public void execute(String doSomething);
    }
    
    
    
    // Enum version
    enum HelloWorld {
        HW0("Hello World0"), HW1("Hello World1"), HW2("Hello World2"), HW3(
                "Hello World3"), HW4("Hello World4"), HW5("Hello World5"), HW6(
                "Hello World6"), HW7("Hello World7"), HW8("Hello World8"), HW9(
                "Hello World9"), HW10("Hello World10"), HW11("Hello World11"), HW12(
                "Hello World12"), HW13("Hello World13"), HW14("Hello World4"), HW15(
                "Hello World15");
    
        private String name = null;
    
        private HelloWorld(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void execute(String doSomething){
            doSomething = "Hello World0";
        }
    
        public static HelloWorld fromString(String input) {
            for (HelloWorld hw : HelloWorld.values()) {
                if (input.equals(hw.getName())) {
                    return hw;
                }
            }
            return null;
        }
    
    }
    
    
    
    
    
    //Enum version for betterment on coding format compare to interface ExecutableClass
    enum HelloWorld1 {
        HW0("Hello World0") {   
            public void execute(String doSomething){
                doSomething = "Hello World0";
            }
        }, 
        HW1("Hello World1"){    
            public void execute(String doSomething){
                doSomething = "Hello World0";
            }
        };
        private String name = null;
    
        private HelloWorld1(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void execute(String doSomething){
        //  super call, nothing here
        }
    }
    
    
    /*
     * http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string
     * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10
     * http://forums.xkcd.com/viewtopic.php?f=11&t=33524
     */ 
    
    0 讨论(0)
  • 2020-11-22 16:44

    For most switch and most if-then-else blocks, I can't imagine that there are any appreciable or significant performance related concerns.

    But here's the thing: if you're using a switch block, its very use suggests that you're switching on a value taken from a set of constants known at compile time. In this case, you really shouldn't be using switch statements at all if you can use an enum with constant-specific methods.

    Compared to a switch statement, an enum provides better type safety and code that is easier to maintain. Enums can be designed so that if a constant is added to the set of constants, your code won't compile without providing a constant-specific method for the new value. On the other hand, forgetting to add a new case to a switch block can sometimes only be caught at run time if you're lucky enough to have set your block up to throw an exception.

    Performance between switch and an enum constant-specific method should not be significantly different, but the latter is more readable, safer, and easier to maintain.

    0 讨论(0)
  • 2020-11-22 16:47

    I totally agree with the opinion that premature optimization is something to avoid.

    But it's true that the Java VM has special bytecodes which could be used for switch()'s.

    See WM Spec (lookupswitch and tableswitch)

    So there could be some performance gains, if the code is part of the performance CPU graph.

    0 讨论(0)
  • 2020-11-22 16:51

    Use switch!

    I hate to maintain if-else-blocks! Have a test:

    public class SpeedTestSwitch
    {
        private static void do1(int loop)
        {
            int temp = 0;
            for (; loop > 0; --loop)
            {
                int r = (int) (Math.random() * 10);
                switch (r)
                {
                    case 0:
                        temp = 9;
                        break;
                    case 1:
                        temp = 8;
                        break;
                    case 2:
                        temp = 7;
                        break;
                    case 3:
                        temp = 6;
                        break;
                    case 4:
                        temp = 5;
                        break;
                    case 5:
                        temp = 4;
                        break;
                    case 6:
                        temp = 3;
                        break;
                    case 7:
                        temp = 2;
                        break;
                    case 8:
                        temp = 1;
                        break;
                    case 9:
                        temp = 0;
                        break;
                }
            }
            System.out.println("ignore: " + temp);
        }
    
        private static void do2(int loop)
        {
            int temp = 0;
            for (; loop > 0; --loop)
            {
                int r = (int) (Math.random() * 10);
                if (r == 0)
                    temp = 9;
                else
                    if (r == 1)
                        temp = 8;
                    else
                        if (r == 2)
                            temp = 7;
                        else
                            if (r == 3)
                                temp = 6;
                            else
                                if (r == 4)
                                    temp = 5;
                                else
                                    if (r == 5)
                                        temp = 4;
                                    else
                                        if (r == 6)
                                            temp = 3;
                                        else
                                            if (r == 7)
                                                temp = 2;
                                            else
                                                if (r == 8)
                                                    temp = 1;
                                                else
                                                    if (r == 9)
                                                        temp = 0;
            }
            System.out.println("ignore: " + temp);
        }
    
        public static void main(String[] args)
        {
            long time;
            int loop = 1 * 100 * 1000 * 1000;
            System.out.println("warming up...");
            do1(loop / 100);
            do2(loop / 100);
    
            System.out.println("start");
    
            // run 1
            System.out.println("switch:");
            time = System.currentTimeMillis();
            do1(loop);
            System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
    
            // run 2
            System.out.println("if/else:");
            time = System.currentTimeMillis();
            do2(loop);
            System.out.println(" -> time needed: " + (System.currentTimeMillis() - time));
        }
    }
    

    My C# standard code for benchmarking

    0 讨论(0)
  • 2020-11-22 16:55

    That's micro optimization and premature optimization, which are evil. Rather worry about readabililty and maintainability of the code in question. If there are more than two if/else blocks glued together or its size is unpredictable, then you may highly consider a switch statement.

    Alternatively, you can also grab Polymorphism. First create some interface:

    public interface Action { 
        void execute(String input);
    }
    

    And get hold of all implementations in some Map. You can do this either statically or dynamically:

    Map<String, Action> actions = new HashMap<String, Action>();
    

    Finally replace the if/else or switch by something like this (leaving trivial checks like nullpointers aside):

    actions.get(name).execute(input);
    

    It might be microslower than if/else or switch, but the code is at least far better maintainable.

    As you're talking about webapplications, you can make use of HttpServletRequest#getPathInfo() as action key (eventually write some more code to split the last part of pathinfo away in a loop until an action is found). You can find here similar answers:

    • Using a custom Servlet oriented framework, too many servlets, is this an issue
    • Java Front Controller

    If you're worrying about Java EE webapplication performance in general, then you may find this article useful as well. There are other areas which gives a much more performance gain than only (micro)optimizing the raw Java code.

    0 讨论(0)
  • 2020-11-22 17:05

    It's extremely unlikely that an if/else or a switch is going to be the source of your performance woes. If you're having performance problems, you should do a performance profiling analysis first to determine where the slow spots are. Premature optimization is the root of all evil!

    Nevertheless, it's possible to talk about the relative performance of switch vs. if/else with the Java compiler optimizations. First note that in Java, switch statements operate on a very limited domain -- integers. In general, you can view a switch statement as follows:

    switch (<condition>) {
       case c_0: ...
       case c_1: ...
       ...
       case c_n: ...
       default: ...
    }
    

    where c_0, c_1, ..., and c_N are integral numbers that are targets of the switch statement, and <condition> must resolve to an integer expression.

    • If this set is "dense" -- that is, (max(ci) + 1 - min(ci)) / n > α, where 0 < k < α < 1, where k is larger than some empirical value, a jump table can be generated, which is highly efficient.

    • If this set is not very dense, but n >= β, a binary search tree can find the target in O(2 * log(n)) which is still efficient too.

    For all other cases, a switch statement is exactly as efficient as the equivalent series of if/else statements. The precise values of α and β depend on a number of factors and are determined by the compiler's code-optimization module.

    Finally, of course, if the domain of <condition> is not the integers, a switch statement is completely useless.

    0 讨论(0)
提交回复
热议问题