134

Lots of Java books describe the switch statement as being faster than the if else statement. But I did not find out anywhere why switch is faster than if.

Example

I have a situation I have to choose any one item out of two. I can use either use

switch (item) {
    case BREAD:
        //eat Bread
        break;
    default:
        //leave the restaurant
}

or

if (item == BREAD) {
    //eat Bread
} else {
    //leave the restaurant
}

considering item and BREAD is a constant int value.

In the above example which is faster in action and why?

Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78
  • Maybe this is an answer also for java: http://stackoverflow.com/questions/767821/is-else-if-faster-than-switch-case/767849#767849 – Tobias Jul 15 '11 at 10:56
  • 22
    In general, from [Wikipedia](http://en.wikipedia.org/wiki/Switch_case): *If the range of input values is identifiably 'small' and has only a few gaps, some compilers that incorporate an optimizer may actually implement the switch statement as a branch table or an array of indexed function pointers instead of a lengthy series of conditional instructions. This allows the switch statement to determine instantly what branch to execute without having to go through a list of comparisons.* – Felix Kling Jul 15 '11 at 10:56
  • I would hope that in most circumstances, an optimising compiler would be able to generate code that had similiar performance characteristics. In any case, you would have to be calling many millions of times to notice any difference. – Mitch Wheat Jul 15 '11 at 10:58
  • @Tobiask both questiona are different. –  Jul 15 '11 at 11:01
  • as the answer suggests that for switch it uses hashmap then it's surely faster but uses additionial memory, which in if then case you don't use any additional memory. – Zemzela Jul 15 '11 at 11:02
  • @Deepakkk: Actually.... they are not. And the answer gives an explanation for why `switch` is faster in general. – Felix Kling Jul 15 '11 at 11:03
  • @Mitch Wheat: It does not. I've just tested it. It could be though that some optimization occures at runtime by JIT but can not prove that. – bezmax Jul 15 '11 at 11:06
  • @Felix Kling : the answer suggests that If a switch contains more than five items, it's implemented using a lookup table or a hash list. But if switch contains one 1 item as i have mentioned in the example –  Jul 15 '11 at 11:07
  • @Max: I said similiar performance, NOT the same code. Did you benchmark? – Mitch Wheat Jul 15 '11 at 11:07
  • @Deepakkk: Maybe you should explicitly state that you are only interested in the case with one item. You know, we normally generalize/abstract problems. Although I tend to say that in this specific case, you will not find any performance differences. – Felix Kling Jul 15 '11 at 11:08
  • @Felix Kling. Thanks Really your response is helpful for me. –  Jul 15 '11 at 11:10
  • 2
    You should be wary of books that make statements like this without explanation/proof/reasoning. – matt b Jul 15 '11 at 12:08
  • The top answer to this **[question](http://stackoverflow.com/questions/338206/switch-statement-with-strings-in-java)** explains it pretty well. This **[article](http://www.artima.com/underthehood/flowP.html)** explains everything pretty well too. – bezmax Jul 15 '11 at 10:56
  • possible duplicate of [What is the relative performance difference of if/else versus switch statement in Java?](http://stackoverflow.com/questions/2086529/what-is-the-relative-performance-difference-of-if-else-versus-switch-statement-i) – Ciro Santilli OurBigBook.com Jun 24 '15 at 13:58
  • Possible duplicate of [Is there any significant difference between using if/else and switch-case in C#?](http://stackoverflow.com/questions/395618/is-there-any-significant-difference-between-using-if-else-and-switch-case-in-c) – Dharvik shah Aug 08 '16 at 05:41

5 Answers5

133

Because there are special bytecodes that allow efficient switch statement evaluation when there are a lot of cases.

If implemented with IF-statements you would have a check, a jump to the next clause, a check, a jump to the next clause and so on. With switch the JVM loads the value to compare and iterates through the value table to find a match, which is faster in most cases.

Daniel
  • 27,718
  • 20
  • 89
  • 133
  • 8
    Doesn't iterate translate to "check, jump"? – fivetwentysix Jul 15 '11 at 11:06
  • 21
    @fivetwentysix: No, refer to this for info: http://www.artima.com/underthehood/flowP.html . Quote from article: When the JVM encounters a tableswitch instruction, it can simply check to see if the key is within the range defined by low and high. If not, it takes the default branch offset. If so, it just subtracts low from key to get an offset into the list of branch offsets. In this manner, it can determine the appropriate branch offset without having to check each case value. – bezmax Jul 15 '11 at 11:11
  • 1
    (i) a `switch` may not be translated into a `tableswitch` bytecode instruction - it may become a `lookupswitch` instruction which performs similarly to an if/else (ii) even a `tableswitch` bytecode instructions may be compiled into a series of if/else by the JIT, depending on factors such as the number of `case`s. – assylias Mar 31 '14 at 13:58
  • 1
    `tableswitch` vs `loopuswitch`: http://stackoverflow.com/questions/10287700/difference-between-jvma-lookupswitch-and-tableswitch – Ciro Santilli OurBigBook.com Jun 24 '15 at 16:35
44

A switch statement is not always faster than an if statement. It scales better than a long list of if-else statements as switch can perform a lookup based on all the values. However, for a short condition it won't be any faster and could be slower.

KSev
  • 1,859
  • 1
  • 24
  • 23
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
19

The current JVM has two kinds of switch byte codes: LookupSwitch and TableSwitch.

Each case in a switch statement has an integer offset, if these offsets are contiguous (or mostly contiguous with no large gaps) (case 0: case 1: case 2, etc.), then TableSwitch is used.

If the offsets are spread out with large gaps (case 0: case 400: case 93748:, etc.), then LookupSwitch is used.

The difference, in short, is that TableSwitch is done in constant time because each value within the range of possible values is given a specific byte-code offset. Thus, when you give the statement an offset of 3, it knows to jump ahead 3 to find the correct branch.

Lookup switch uses a binary search to find the correct code branch. This runs in O(log n) time, which is still good, but not the best.

For more information on this, see here: Difference between JVM's LookupSwitch and TableSwitch?

So as far as which one is fastest, use this approach: If you have 3 or more cases whose values are consecutive or nearly consecutive, always use a switch.

If you have 2 cases, use an if statement.

For any other situation, switch is most likely faster, but it's not guaranteed, since the binary-search in LookupSwitch could hit a bad scenario.

Also, keep in mind that the JVM will run JIT optimizations on if statements that will try to place the hottest branch first in the code. This is called "Branch Prediction". For more information on this, see here: https://dzone.com/articles/branch-prediction-in-java

Your experiences may vary. I don't know that the JVM doesn't run a similar optimization on LookupSwitch, but I've learned to trust the JIT optimizations and not try to outsmart the compiler.

HesNotTheStig
  • 549
  • 1
  • 4
  • 9
  • 2
    Since posting this, it has come to my attention that "switch expressions" and "pattern matching" are coming to Java, possibly as soon as Java 12. http://openjdk.java.net/jeps/325 http://openjdk.java.net/jeps/305 Nothing is concrete yet, but it appears that these will make `switch` an even more powerful language feature. Pattern matching, for example, will allow for much smoother and performant `instanceof` lookups. However, I think it's safe to assume that for basic switch/if scenarios, the rule I mentioned will still apply. – HesNotTheStig Sep 27 '18 at 17:17
  • In my performance tests if-else is comparable or even faster than switch most of the times. Who knows, how it works if java uses LookupSwitch and the program flow just matched one case and then continues to execute till the bottom(no breaks). Is it kind of binary tree that has in-order links? – the_kaba Nov 24 '22 at 22:29
1

So if you plan to have loads of packets memory isn't really a large cost these days and arrays are pretty fast. You also cannot rely on a switch statement to auto generate a jump table and as such it's easier to generate the jump table scenario yourself. As you can see in below example we assume a maximum of 255 packets.

To get the below result your need abstraction.. i'm not going to explain how that works so hopefully you have an understanding of that.

I updated this to set the packet size to 255 if you need more then that you'll have to do a bounds check for (id < 0) || (id > length).

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);
}

Edit since I use a Jump Table in C++ a lot now i'll show an example of a function pointer jump table. This is a very generic example, but I did run it and it works correctly. Keep in mind you must set the pointer to NULL, C++ will not do this automatically like in Java.

#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.\n";
}

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

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;
}

Also another point i'd like to bring up is the famous Divide and Conquer. So my above 255 array idea could be reduced to no more then 8 if statements as a worst case scenario.

I.e. but keep in mind it get's messy and hard to manage fast and my other approach is generally better, but this is utilize in cases where arrays just won't cut it. You have to figure out your use case and when each situation works best. Just as you wouldn't want to use either of these approaches if you only have a few checks.

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 {

                              }
                          }
                      }
                  }
             }      
        }
   }
}
Jeremy Trifilo
  • 456
  • 6
  • 11
  • 2
    Why the double indirection? Since ID must be constrained anyway, why not just bounds-check the incoming id as `0 <= id < packets.length` and ensure `packets[id]!=null` and then do `packets[id].execute(data)`? – Lawrence Dol Jul 30 '14 at 18:02
  • yeah sorry for late response looked at this again.. and I have no idea what the hell I was thinking I updated the post lol and capped the packets to the size of a unsigned byte so no length checks are needed. – Jeremy Trifilo Feb 02 '19 at 21:31
0

At the bytecode level, subject variable is loaded only once into processor register from a memory address in the structured .class file loaded by Runtime,and this is in a switch statement; whereas in an if-statement, a different jvm instruction is produced by your code-compiling DE, and this requires that each variable be loaded in to registers although same variable is used as in next preceeding if-statement. If you know of coding in assembly language then this would be commonplace; although java compiled coxes are not bytecode, or direct machine code, the conditional concept hereof is still consistent. Well, I tried to avoid deeper technicality upon explaining. I hope I had made the concept clear and demystified. Thank you.