Java中if/else与switch语句的相对性能差异是什么?

Ant*_*th0 118 java performance if-statement switch-statement

担心我的网络应用程序的性能,我想知道哪个"if/else"或switch语句在性能方面更好?

Wav*_*ick 121

我完全同意过早优化是需要避免的.

但是,Java VM确实有特殊的字节码可以用于switch().

参见WM Spec(lookupswitchtableswitch)

因此,如果代码是性能CPU图的一部分,那么可能会有一些性能提升.

  • 我想知道为什么这个评论没有被评为更高:它是所有这些评论中最具信息性的.我的意思是:我们都已经知道过早优化是不好的,所以,不需要解释第1000次. (57认同)
  • +1从http://stackoverflow.com/a/15621602/89818开始,似乎性能提升确实存在,如果您使用18个以上的案例,您应该会看到优势. (4认同)

Bal*_*usC 103

这是微观优化和过早优化,这是邪恶的.而是担心有问题的代码的可读性和可维护性.如果if/else胶合在一起的块数超过两个或其大小不可预测,那么您可能会高度考虑一个switch声明.

或者,您也可以获取多态性.首先创建一些界面:

public interface Action { 
    void execute(String input);
}
Run Code Online (Sandbox Code Playgroud)

并掌握所有实现Map.您可以静态或动态地执行此操作:

Map<String, Action> actions = new HashMap<String, Action>();
Run Code Online (Sandbox Code Playgroud)

最后替换if/elseswitch通过类似的东西(留下无效的检查,如无效指针):

actions.get(name).execute(input);
Run Code Online (Sandbox Code Playgroud)

可能if/else或者微小switch,但代码至少可以更好地维护.

当您谈论Web应用程序时,您可以使用HttpServletRequest#getPathInfo()as操作键(最终编写一些代码以在循环中将pathinfo的最后部分分开,直到找到操作).你可以在这里找到类似的答案:

如果您一般担心Java EE Web应用程序的性能,那么您可能会发现本文也很有用.除了(微)优化原始Java代码之外,还有其他领域提供了更多的性能提升.

  • 我不是很快就把所有早期优化都视为"邪恶".过于咄咄逼人是愚蠢的,但是当面对具有可比较的可读性的结构时,选择一个已知表现更好的结构是一个合适的决定. (71认同)
  • 与tablewitsch指令相比,HashMap查找版本可以轻松地慢10倍.我不会称之为"微型"! (8认同)
  • 我对在交换语句的一般情况下实际知道Java的内部工作感兴趣 - 我不知道是否有人认为这与过度优化早期优化有关.话虽这么说,我完全不知道为什么这个答案得到了如此多的支持,以及为什么它是公认的答案......这没有回答最初的问题. (7认同)

Joh*_*lla 50

if/else或switch很可能成为你性能问题的根源.如果您遇到性能问题,应首先进行性能分析分析,以确定慢点的位置.过早优化是万恶之源!

然而,有可能谈论switch与if/else与Java编译器优化的相对性能.首先请注意,在Java中,switch语句在非常有限的域上运行 - 整数.通常,您可以按如下方式查看switch语句:

switch (<condition>) {
   case c_0: ...
   case c_1: ...
   ...
   case c_n: ...
   default: ...
}
Run Code Online (Sandbox Code Playgroud)

where c_0,,c_1...和c_N是作为switch语句的目标<condition>的整数,并且必须解析为整数表达式.

  • 如果这个集合是"密集的" - 即(max(c i)+ 1 - min(c i))/ n>α,其中0 <k <α<1,其中k大于某个经验值,a可以生成跳转表,这是非常高效的.

  • 如果此集合不是非常密集,但是n> =β,则二叉搜索树可以在O(2*log(n))中找到目标,这仍然是有效的.

对于所有其他情况,switch语句与等效的if/else语句系列完全一样有效.α和β的精确值取决于许多因素,并由编译器的代码优化模块确定.

最后,当然,如果域<condition>不是整数,则switch语句完全没用.

  • 应该注意的是,交换机的工作不仅仅是整数.从Java教程:"一个开关使用byte,short,char和int原始数据类型.它也适用于枚举类型(在枚举类型中讨论),String类,以及一些包装某些基本类型的特殊类:字符,字节,短整数和整数(在数字和字符串中讨论)." 支持String是最近添加的; 在Java 7中添加.http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html (2认同)

Bit*_*lue 10

使用开关!

我讨厌维护if-else-blocks!做一个测试:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

我的C#标准代码用于基准测试

  • 测试中的主要成本是随机数生成。我修改了测试以在循环之前生成随机数,并使用临时值反馈到 r 中。那么 switch 的速度几乎是 if-else 链的两倍。 (2认同)

Jim*_*ans 8

据Cliff点击2009年Java One谈现代硬件崩溃课程:

今天,性能主要受到内存访问模式的支配.缓存未命中占主导地位 - 内存是新磁盘.[幻灯片65]

你可以在这里获得他的完整幻灯片.

Cliff给出了一个例子(在幻灯片30上完成),表明即使CPU正在执行寄存器重命名,分支预测和推测执行,它也只能在4个时钟周期内启动7次操作,然后由于两次缓存未命中而需要阻塞返回300个时钟周期.

所以他说要加速你的程序你不应该考虑这种小问题,而是考虑更大的问题,例如你是否正在进行不必要的数据格式转换,例如转换"SOAP→XML→DOM→SQL→......" "which"通过缓存传递所有数据".


mal*_*ere 8

我记得读过Java字节码中有两种Switch语句.(我认为它是'Java性能调优'一个是一个非常快速的实现,它使用switch语句的整数值来知道要执行的代码的偏移量.这将要求所有整数是连续的并且在明确定义的范围内我猜测使用Enum的所有值也会属于那个类别.

我同意许多其他海报...虽然这是非常热门的代码,但担心这个可能为时过早.

  • +1热门代码评论.如果它在你的主循环中它不是过早的. (4认同)

Kan*_*mar 5

在我的测试中,更好的性能是Windows7 中的ENUM > MAP > SWITCH > IF/ELSE IF

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
 */ 
Run Code Online (Sandbox Code Playgroud)