Wednesday, August 4, 2021

How String in Switch works in Java? Example

Ever Since Java allows using String variables in switch and case statements, there are many programmers using this feature in code, which can be better written using integer and enum patterns. This was one of the popular features of the JDK 7 release, including automatic resource management and multi-exception catch blocks. Though I upfront didn't like this feature because of the better alternatives available in terms of using the enumeration type, I am not totally against this feature. One reason for this is convenience and given usage of String in Java program, it's quite handy as well, but I prefer to learn more before using any new feature in production code.

When I first come to know about this feature, I had an idea that String in Switch can be implemented using equals() and hashCode() method, I was more interested in how String in Switch works in Java 7.

One more reason I was curious to know about the internal working of this feature is because I wanted to ask this during Java interviews, having one such question makes interviews a little more interesting. Testing was simple, you just need to write code using String variables in the switch block, and then decompile the code to see, how the compiler has translated them.

So what are we waiting for, Let's see how String in switch block actually works?



Original Code :

This is the simple test program, which has a main method and a switch block, which is operating on String variable. String argument is provided at the time of running this program, which is then accessed from String array argument of main method.



We have three modes to start our application, Active, Passive and Safe. Though its better to use Enum to represent such kind of well known fixed values, if you decided to use String, make sure you write it down in capital case to avoid case-sensitive issue with lower case and camel case.

You can also see this tutorial to learn more about correctly using String in switch expressions in Java SE 7.

/**
  * Java Program to demonstrate how string in switch functionality 
  * is implemented in 
  * Java SE 7 release. 
  */
public class StringInSwitchCase{

    public static void main(String[] args) {

        String mode = args[0];

        switch (mode) {
            case "ACTIVE":
                System.out.println("Application is running on Active mode");
                break;
            case "PASSIVE":
                System.out.println("Application is running on Passive mode");
                break;
            case "SAFE":
                System.out.println("Application is running on Safe mode");
        }
    }
}
You need to install JDK 1.7 to compile and run this code. You can use any version of JDK 7.


Decompiled Code :

This is the decompiled version of the above class after being compiled on jdk1.7.0_40 version. If you are new in Java and want to learn how to decompile Java class file for reverse engineering, see that post.

Since with every new release we are getting more and more syntactic sugar, knowing how to decompile a class has become very important for all levels of Java programmers. The gap between code you wrote and what gets executed is widening very fast.

Basic knowledge of Java class file format and byte code instruction will only be going to help yours.  Java 8 recently released a key feature called lambda expression also takes helps of the compiler to implement Anonymous class internally, you can decompile your class file to see methods added by the compiler.

/**
  * Reverse Engineered code to show how String in Switch works in Java.
  */ 
import java.io.PrintStream;

public class StringInSwitchCase{

    public StringInSwitchCase() {
    }

    public static void main(string args[]) {
        String mode = args[0];
        String s;
        switch ((s = mode).hashCode()) {
            default:
                break;
            case -74056953:
                if (s.equals("PASSIVE")) {
                    System.out.println("Application is running on Passive mode");
                }
                break;
            case 2537357:
                if (s.equals("SAFE")) {
                    System.out.println("Application is running on Safe mode");
                }
                break;
            case 1925346054:
                if (s.equals("ACTIVE")) {
                    System.out.println("Application is running on Active mode");
                }
                break;
        }
    }
}

If you at this code, you will find out that String in Switch works by using hashCode() and equals() method. Remember, we can only use integer variable in switch case i.e. variable of type byte, short, char, and int. 

Good thing is return type of hashCode() method is int, not long. By the way this one way to remember this fact as well, which I often forget/get confused by myself. If you look closely, switch is on hash code and then a safety check by comparing String with equals() method, this check is required because two unequal object can have same hash code.

So performance wise, it is  not as fast as using enum constants on switch case or using pure integer constant on switch, but its not too bad at all. Since Java compiler is only using one additional method equals(), which can be very fast if you are comparing String literals i.e. when "abc" == "abc".

If you are also considering about calling to hashCode() method, yes that is another 1 time additional cost, because once created, String cache there hash code, as discussed on my favorite article why String is immutable in Java.


So the cost of calling hashCode, will not be significant if this switch case is used in a tight loop e.g. loop to process items or game engine loops to render screens. Nevertheless, I still consider using String in the switch statement and using it to represent a fixed number of things is not a good practice, Enumeration type in Java is there for a reason, and every Java programmer must use it.


How String in Switch case works in Java 7



That's all on How String in Switch works in Java 7. As expected it uses the hashCode() method for switching and equals() method for verification, This means it's just syntactic sugar, rather than an inbuilt native functionality. Now the choice is yours, I am personally not a big fan of using String in Switch case as it results in brittle code, case-sensitive issues, and no compile-time check for invalid input.

In fact, plain old integer constants are my favorite for performance-critical code and Enumeration type in Java, where readability and code quality are more important. In fact, in 99.99% of cases, enum is a better choice than String or integer variable, it's the very same reason they exist in the Java programming language.

All this feature has done is promoted these bad coding practices, I struggle hard to find a proper use case of using String in switch cases with a set of inputs for any other purpose than testing and debugging, let me know if you have a convincing reason of using String in switch case in your project, maybe that will change my mind.


6 comments:

  1. Thank you. It is very good to know such details.

    The performance of string in switch should be slightly better than using plain if-else chain.

    ReplyDelete
  2. Nice article. It made me think about what happens if there's a hash code collision on two strings in the switch. Here's the result: http://blog.tremblay.pro/2014/05/collisions-on-switch-on-strings.html

    ReplyDelete
  3. "New feature" represented by old technique. Just like lambda expressions (if you checked decompiled code).

    ReplyDelete
  4. What I didn't understand in the reverse engineered code above is why mode is assigned to var s and s used in the equals check inside the switch. mode might as well be directly used for the equals check right? Any answers or views on this?

    ReplyDelete
  5. @girish: The expression that you switch upon is not necessarily a local variable or a field, it can be any expression evaluating to a string, for example a method invocation. As the value is used at least twice, it must be saved to a local variable because the result of the method invocation might change over time. If the expression is a field, its value might be changed by a thread running concurrently.

    ReplyDelete
  6. I was once asked, what happens if we pass null to a switch case, written using String? from the decompiled byte code its clear that it will throw NullPointerException because it call hashcode() method without checking for nulls.

    ReplyDelete