关于Java:为什么switch比IF要快

Why switch is faster than if

我在Java中发现了很多书,说开关语句比if语句快。但我没有找到安特在哪里说为什么转换比如果快。

例子

我有一种情况,我必须从两个选项中选择一个,我可以使用以下任何一种方法

1
2
3
4
5
6
7
8
9
switch(item){

case BREAD:
     //eat Bread
break;
default:
    //leave the restaurant

}

或者使用如下if语句

1
2
3
4
5
if(item== BREAD){
//eat Bread
}else{
//leave the restaurant
}

考虑到项目和面包是常量int值

在上面的例子中,哪个动作更快,为什么?


这是因为有特殊的字节码开关语句允许高效评价时,有很多实例。

如果一个if语句实现,你会有一个跳跃到下一个检查,检查条款,a,a跳到下一条款和等等。一个开关的JVM负载的价值和价值的iterates通想把A表找到匹配的速度,这是在大多数情况下。


a switch语句是不总是快比在if语句。它的鳞片比a长if-else语句列表是基于CAN switch将查找所有的值。然而,一个短的状态它不会将任何更快的和可能的。


当前的JVM有两种交换字节码:lookupswitch和tableswitch。

switch语句中的每个case都有一个整数偏移量,如果这些偏移量是连续的(或大部分是连续的,没有大的间隙)(case 0:case 1:case 2,等等),则使用tableswitch。

如果偏移量分布的间隙较大(情况0:情况400:情况93748等),则使用查找开关。

简而言之,不同之处在于TableSwitch是在恒定时间内完成的,因为可能值范围内的每个值都有一个特定的字节代码偏移量。因此,当您给语句一个3的偏移量时,它知道向前跳3以找到正确的分支。

lookup开关使用二进制搜索来查找正确的代码分支。这在O(log n)时间内运行,这仍然很好,但不是最好的。

有关这方面的更多信息,请参见这里:JVM的lookupswitch和tableswitch之间的区别?

至于哪一个最快,使用以下方法:如果您有3个或更多的案例的值是连续的或接近连续的,请始终使用开关。

如果您有两种情况,请使用if语句。

对于任何其他情况,切换都可能更快,但不能保证,因为lookupswitch中的二进制搜索可能会遇到坏的情况。

另外,请记住,JVM将对if语句运行JIT优化,这些语句将尝试将最热的分支放在代码的第一位。这被称为"分支预测"。有关更多信息,请参阅:https://dzone.com/articles/branch-prediction-in-java

你的经历可能会有所不同。我不知道JVM在lookupswitch上没有运行类似的优化,但我已经学会了信任JIT优化,而不是试图胜过编译器。


如果你有这样的计划看上去是不是真的一包的存储成本和天大阵几乎是漂亮的。你不能依赖开关语句自动生成在一个跳转表A和AS生成更容易搜索到它自己的跳表的情况下。你可以在下面的例子,我们假设A湖最大255包。

下面是你需要得到的结果抽象。我需要的是这样的知识去解释作品是在希望你理解。

一个更新包大小设置到这,如果你需要更多的255,那么你就必须做检查(A<0激活ID(ID)| |>长度)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Packets[] packets = new Packets[255];

static {
     packets[0] = new Login(6);
     packets[2] = new Logout(8);
     packets[4] = new GetMessage(1);
     packets[8] = new AddFriend(0);
     packets[11] = new JoinGroupChat(7); // etc... not going to finish.
}

public void handlePacket(IncomingData data)
{
    int id = data.readByte() & 0xFF; //Secure value to 0-255.

    if (packet[id] == null)
        return; //Leave if packet is unhandled.

    packets[id].execute(data);
}

自从我一跳表的编辑使用的焊料在C + +的例子,现在我要表演一跳表函数指针。这是一个非常通用的例子,但它的运行是正确的。记住,你必须设置一个C + +指针为零,不这样做自动的Java类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>

struct Packet
{
    void(*execute)() = NULL;
};

Packet incoming_packet[255];
uint8_t test_value = 0;

void A()
{
    std::cout <<"I'm the 1st test.
"
;
}

void B()
{
    std::cout <<"I'm the 2nd test.
"
;
}

void Empty()
{

}

void Update()
{
    if (incoming_packet[test_value].execute == NULL)
        return;

    incoming_packet[test_value].execute();
}

void InitializePackets()
{
    incoming_packet[0].execute = A;
    incoming_packet[2].execute = B;
    incoming_packet[6].execute = A;
    incoming_packet[9].execute = Empty;
}

int main()
{
    InitializePackets();

    for (int i = 0; i < 512; ++i)
    {
        Update();
        ++test_value;
    }
    system("pause");
    return 0;
}

这一点我想带上是著名的鸿沟和征服。我以上的想法可以被精简到255阵列8没有更多然后if语句作为一个最坏的情况下。

记住它。但得到的混乱和硬设法和我的其他的方法几乎是不可能的,这是更好的情况下,使用在WHERE数组只是不切它。你必须找到你的作品,当每个用例情况最佳。只是你不想使用任何方法如果你只能进行一些检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
If (Value >= 128)
{
   if (Value >= 192)
   {
        if (Value >= 224)
        {
             if (Value >= 240)
             {
                  if (Value >= 248)
                  {
                      if (Value >= 252)
                      {
                          if (Value >= 254)
                          {
                              if (value == 255)
                              {

                              } else {

                              }
                          }
                      }
                  }
             }      
        }
   }
}


在字节码级的主题是只读一次的变量,加载到寄存器的内存地址从一个处理器在运行时加载的文件的结构化的类,这是在一个switch语句;而在if语句中,指令是由一个不同的虚拟机代码编译,这需要在每个变量被加载。Al对登记册虽然相同的变量是用来作为下一preceeding if语句。如果你知道在汇编语言编码在此将commonplace;虽然Java字节码编译coxes是不直接的,或机器代码,hereof条件概念是不一致的。好的,我想避免在technicality统一解释。我希望我有一个清晰的概念和demystified制造。谢谢你。