I'm reluctant to use a switch, but I saw switch will be improved in Java 12
Java 12 added the switch expression as an experimental feature. A Java switch expression is a switch statement which can return a value.
The only use case I found (before Java 12) where switch may be useful is returning different values from a small closed set of cases, e.g.:
switch (input) {
case "A":
return "1";
case "B":
return "2";
default:
return "0";
}
Or in Java 12 example:
return
switch(digitInDecimal){
case 0 -> '0';
case 1 -> '1';
case 2 -> '2';
default -> '?';
But I found an old but high-ranked answer that says to avoid multiple return statements:
Assigning a value to a local variable and then returning that at the end is considered a good practice. Methods having multiple exits are harder to debug and can be difficult to read.
So I wonder, is that answer still relevant due to switch changes?
Must I wait for Java 12 where switch can be used without temporary variables and breaks?
Assigning a value to a local variable and then returning that at the end is considered a good practice.
I have no idea when it was considered a good practice. To me, switch is usually * an indicator that a design error was made. I would rather put my effort into thinking how to avoid a switch than into wondering how to return a value from a switch.
A few examples
Long list of if statements in Java
How to avoid switch-case statements in Java
Converting many 'if else' statements to a cleaner approach
Methods having multiple exits are harder to debug and can be difficult to read.
The same goes for a method that has a lot of breaks - that's what you are going to do if you choose the "local-variable approach".
In my opinion, none of these
// 1
switch (input) {
case "A":
return "1";
case "B":
return "2";
default:
return "0";
}
// 2
String varibleToReturn = null;
switch (input) {
case "A":
varibleToReturn = "1";
break;
case "B":
varibleToReturn = "2";
break;
default:
varibleToReturn = "0";
}
return varibleToReturn;
// 3
return switch(digitInDecimal) {
case 0 -> '0';
case 1 -> '1';
case 2 -> '2';
default -> '?';
}
makes a significant difference, or a slight improvement. Yes, Java-12's switch would give more conciseness and expressiveness, but the fundamental idea remains the same.
Must I wait for Java 12 where switch can be used without temporary variables and breaks?
What does it mean? :) No, the deadline is tomorrow, you have to work with what you've got at hand now.
*I am not underestimating the usefulness of switch. It may come in handy, for instance, when you programme at low-level, or you write an optimization.
I am just saying that in the real world, with Springs, and Hibernates, in a world of patterns, switch is obsolescent.
But I found an old but high-ranked answer that says to avoid multiple
return statements:
Assigning a value to a local variable and then returning that at the
end is considered a good practice. Methods having multiple exits are
harder to debug and can be difficult to read.
So I wonder, is that answer still relevant due to switch changes?
This is a common misconception, it originates form the phrase: "Single entry, single exit." (Page 24) All this originates from an other era, one that lead to structured programming languages and eventually to object oriented programming languages (like Java).
Don't worry about multiple return statements, there is nothing wrong with it.
Related
This question already has answers here:
What are switch expressions and how are they different from switch statements?
(4 answers)
Closed 1 year ago.
I've been working with switch statements in my latest project and wanted to have two possible values execute the same code. So I looked up methods on how do that. Quite the only answer I found looks like this:
switch (element) {
case hi:
case hello:
//do something here
break;
}
But I experimented a bit more and found that my IDE doesn't complain when I did:
switch (element) {
case hi, hello:
//do something here
break;
}
So I tried it out and found that it works. So I was wondering if there is something wrong with using this method as I found nothing on it online. I would just love to be able to use it as it looks so much cleaner.
Recent versions of java (12 and up) have been putting in a ton of work on switch. There are now quite a few forms. Some of these forms have an underlying principle that strongly suggests some new syntactical feature is important, and for consistency's sake, if that feature can also be allowed for the other forms, the other forms are updated too.
History
The original java 1.0 switch is a straight copy from C (which explains their utterly bizarre and silly syntax; it's what C had, and java was designed to be comfortable to C coders. We can whinge about the fact that this means the syntax is daft, but it's hard to deny that the C style syntax, no matter how crazy it seems, has taken over the planet, with C, C#, Java, and Javascript added together taking up a huge chunk of the 'market' so to speak).
It:
Required that the thing you switch is on an int and only an int
That each case is a specific and constant number (no case 10-100, and definitely no case <5 or case x:).
It's a statement (The switch itself doesn't have a value, it's a way to conditionally jump to a statement).
Unlike the C version, treating the switch's nature as a weird GOTO-esque construct and thus allowing you to interleave a control structure such as a do/while loop through it is not actually allowed (fortunately, I guess)? - So, Duff's Device is not and has never been legal in java.
Recent updates:
Together with enums: Enums in switches
Java1.5 brought enums, and as enums are intended to replace int constants, they needed to be usable in switches. Given that enum values have an 'ordinal' which is an int, the implementation is trivial: switch (someEnum) { case ENUMVAL: } is light sugar around switch(someEnum.ordinal()) { case 1: }, where 1 is the ordinal value of ENUMVAL.
Only semi-recent: Strings
In JDK7, strings-in-switch support was added. The same principles apply: Only constants, null is not okay - and it's 'fast', in that its implemented as a jump table (contrast to a sequence of if/elseif statements, which requires a comparison for each one. A jump table just jumps, using a single comparison, straight to the right line or to the end if none match.
Now the new stuff (12+): As an expression
These days switch can be an expression. As in, the switch returns a thing. However, in order to make that work, each and every 'path' throughout your switch must return something, and it cannot possibly be the case (heh) that there is a missing case statement; after all, what should the expression resolve to then? Thus, in this 'mode', you must have exhausted all options. Interesting subcase of this is enums, where you can be 'exhaustive', and yet at runtime this can never be guaranteed; what if someone adds a value to an enum and recompiles JUST the enum and drops that in there? Now an erstwhile exhaustive (covers every option) switch no longer is:
int y = switch(textyNumber) {
case "one": yield 1;
case "two": yield 2;
default: yield 0;
};
yield is a new approach here. It's a lot like 'return' - it 'returns' from the switch, returning that value. It is a compiler error if that default clause is missing.
The weirdness with enums means that if you're exhaustive (cover every enum value), it compiles, but if you then add an enum value, do not recompile the code with the switch, and attempt to toss a newly created enum value through, the switch throws a java.lang.IncompatibleClassChangeError. The more you know.
arrow syntax
Because 'yield' is a tad unwieldy and it can be useful to just one-liner the desired expression, similar to how you can write e.g.:
Comparator<String> byLength = (a, b) -> a.length() - b.length();
that same arrow syntax is now legal in switches, and for consistency, it's even legal in statement-form switches:
int x = 1;
int y = switch(x) {
case 1 -> 2;
default -> 0;
};
switch (y) {
case 0 -> System.out.println("Interesting");
}
The arrow prevents fallthrough (it implies break), and takes only one statement. But a whole block also counts as one statement:
switch (y) {
case 0 -> {
System.out.println("one");
System.out.println("two");
}
case 1 -> {
System.out.println("three");
}
}
would print just 'one', 'two'.
multi-case
Commas can be used to separate values; this is similar to how you can use bars to separate exception types (catch (IOException | SQLException e) { ... }. Applies to all forms.
pattern matching
Now we get to the real intriguing stuff:
record Point(int x, int y) {}
...
Object p = new Point(10, 20);
switch (p) {
case Point(x, y) -> System.out.printf("Point [%d, %d]", x, y);
}
will soon be legal java! Now the thing that follows the 'case' is a 'template'. Can you smash whatever p resolves to into this template? If yes, the case matches, and the variables are filled in for you. This works with 'deconstructable' types (currently: only records; in future any type can provide a deconstructor), as well as an instanceof kinda deal:
Object o = someExpression;
switch (o) {
case Number n -> System.out.println("is a number. intvalue: " + n.intValue());
case String s -> System.out.println("is a string: " + s.toLowerCase());
}
I think that covers all the updates that are available for switch up to java v 17 and a bit beyond it, even.
NB: You find this info in extreme detail and as early as you want it by following the appropriate openjdk dev mailing lists. This stuff would be found mostly on amber-dev, with some of the pattern matching stuff found on valhalla-dev due to being closely associated with records, and records overlaps with value types (valhalla = value types for java, amber = general language updates). At java conferences somebody will usually give a breakdown of where this stands, reddit's /r/java tends to post it when someone from the OpenJDK team (for this stuff, you're looking at Brian Goetz generally) posts a major update on how the feature is envisioned / has been implemented. Follow Brian on twitter, that helps too. The release notes, and the umbrella JEP page associated with any new java release should also mention this stuff with links you can then follow.
Your original code segment uses fall-through to give the same response for both cases; some people find this difficult to read and follow. Java 12 gave us two related features that people find help clean up their switch statements, one of which you've discovered here:
cases can be comma separated rather than relying on fall-through
the arrow label can be used instead of the colon and break; commands.
Therefore, if you find yourself not liking fall-through, and not wanting to worry about whether you remembered your break commands, you can write the switch as follows:
switch (element) {
case hi, hello -> // do something here
case goodbye, bye -> // do something else here
}
Notice that in this case, we don't have any break statements, but there is no fall-through between the hi case and the bye case; they are separate due to the arrow label.
This is not available in Java, However You can do this thing with Kotlin, which is 100% Compatible with Java
Usually, in problems that I encounter in school, there are ones that the expects us to use switch cases. Though I always use arrays as alternative for this kind of problem for the sake of shorter codes.
So a solution with Switch-Statement will look like this:
String day;
int dayNum = n%7; //n is an input
switch(dayNum){
case 0:
day = "Sunday";
break;
case 1:
day = "Monday";
break;
case 2:
day = "Tuesday";
break;
case 3:
day = "Wednesday";
break;
case 4:
day = "Thursday";
break;
case 5:
day = "Friday";
break;
case 6:
day = "Saturday";
break;
}
System.out.println("Today is " + day);
And a solution with array as alternative for this looks like this:
int dayNum = n%7; //n is an input
String[] day = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
System.out.println("Today is " + day[dayNum]);
The second example is the kind of code I usually prefer to use.
What I'd like to ask is how does the 2 solutions compare when it comes to memory and time complexity?
The code you wrote is just bad use of switches.
switches are great when one, or preferably both, of these things holds:
The values you are triggering on aren't consecutive (i.e. case 384:, then case 30491:, etcetera. Trivial example is when switching on a string for a certain command.
The thing you want to do is a bunch of code (vs. returning a single result, as here).
For what its worth, you've also written the case block in a rather convoluted fashion. If you have a separate method for this, you could write:
switch (foo) {
case 0: return "Sunday";
case 1: return "Monday";
}
Also (new in.. java 14?), you can write this too:
String dayName = switch(foo) {
case 0 -> "Sunday";
case 1 -> "Monday";
default -> throw new IllegalArgumentException("Unknown day");
};
Lastly, this code should not exist. Because java.time.DayOfWeek already exists. You've badly reinvented the wheel.
Performance
When talking about 7 elements it just doesn't matter. Other factors dominate, simple as that.
As a general principle (which doesn't kick in until many many hundreds, maybe thousands, see footnote!), the reason your array-based code is linear as you wrote it is solely because java would not (currently; future java changes are afoot that may change this) realize that the line that 'makes' your string array with day names can be executed once for the lifetime of a JVM boot and never again; in other words, this code, under the hood, compiles down to making a new string array with 7 slots and then assigning 7 constants to the slots (those constants are the references to the strings, which are calculated "once per VM lifetime").
If you move that array into a static field, that goes away and they're both O(1) performance (the same 'speed' whether you have 7 days or 7 million days), because they both just do a single lookup. No looping.
IMPORTANT PERFORMANCE FOOTNOTE: Computers are incredibly complex and act nothing like a von neumann model. Predicting the performance of code based on common sense therefore usually steers you extremely wrong. There are therefore 3 important rules to keep in mind:
Write code the way java programmers the world over tend to write it, and keep things flexible. IF performance issues occur, you will probably need to change the relevant part of your app. This is easier if your code is flexible. Most rules of thumb about performance you read about make your code less flexible and are therefore bad! - also, the JVM optimizes code, and does this essentially by being a giant pattern matching machine, detecting patterns it knows how to run well. The programmers who make this pattern matching optimizing machine tend to write patterns that are commonly used in java code. Thus, idiomatic java code tends to be fast in practice.
Nobody can look at code and just know how it ends up performing in real life, because it's too complex. Don't take my word for it; the very engineers that write the VM say this all the time. If they can't figure it out, you stand no chance whatsoever. Therefore, a profiler report or JMH timing result is required before even thinking about letting code performance ideas determine how you write your code. See the rule above: Without this, the best, fastest code is the cleanest, most flexible, most easy to read code.
The one exception is algorithmic complexity (big O notation). This is a complex topic (generally, university-level informatics and very math heavy), well beyond an SO answer, but plenty of online resources can be found if you want to dive in. But do note that the big-O factor often doesn't start controlling until you get a ton of data. For example, in this case technically your array-based code is O(n) (because you create the array inside the code, every time you run it), whereas the switch is O(1), but given that n is 7 here, that's irrelevant. It probably won't get relevant until you get to hundreds of items.
I'd like to know if there is any efficiency difference between using if statement or switch. For example:
if(){
//code
}
else if(){
//code
}
else{
//code
}
I believe that program needs to go and check all of the if statement even if the first if statement was true.
switch(i){
case 1:
//code
break;
case 2:
//code
break;
But in the switch, there is a break command. Is my approaching right? If not, could you explain the efficiency difference between them?
Switch perf is better than if else as in case of switch there will be one time evaluation . Once it evaluated the switch it knows which case needs to be executed but in case of if else it has to go through all conditions in case of worst scenario.
The longer the list condition, better will be switch performance but for shorter list (just two conditions), it can be slower also
From Why switch is faster than if
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
Switch is faster.
Imagine you are at an intersection, with many paths.
With switch, you go to the right path at the first time.
With if, then you have to try all the paths before you find the right one.
Use switch whenever possible.
Of course, for computer this difference is very small that you don't even notice. But yeah, you get the point.
I think the code is quite clear. With if, you have to check each case and after case by case (in the worst case, last return gives back the result). With switch, some kind like a special byte code checking and jump to the correct case to return. So the switch is a bit faster than the if statement. However, I think we need to focus on the way we implement for easier to read. In some simple case, the if is also a choice to write code.
Can anyone explain what the trade off (even if it's negligible) would be between using if, if else, or switch in a sizable block of code similar to the following? Is the situation different if it were comparing a String or another Object instead of an int? The examples are in Java but it is meant as a general question.
EDIT
As several answers stated, a switch is going to be faster and should probably be used if there are more than a few cases. However, nobody has commented on if vs if else when in a long chain like this. What sparked this question is that I am frequently creating these blocks where a switch can't be used because most of the cases require multiple expressions. I guess excluding the else feels sloppy, but it isn't really necessary so why include it?
public String getValueString(int x) {
if (x == 1) return "one";
if (x == 2) return "two";
if (x == 3) return "three";
if (x == 4) return "four";
...
return null;
}
VS
public String getValueString(int x) {
if (x == 1) return "one";
else if (x == 2) return "two";
else if (x == 3) return "three";
else if (x == 4) return "four";
...
return null;
}
VS
public String getValueString(int x) {
switch(x) {
case 1: return "one";
case 2: return "two";
case 3: return "three";
case 4: return "four";
...
}
return null;
}
If you have a lot of cases, then the switch approach is the preferred method. The reason is because the first two requires essentially a linear search through all the if-statements. So it is O(N) to the number of cases you have.
On the other hand, switch statements are optimized differently and can be either O(log(N)) or even O(1) for finding that correct case.
How can the compiler achieve O(log(N)) or even O(1)?
Binary search of the case values will allow it to be done in O(log(N)).
If the case values are dense enough, the compiler may even use a jump table indexed by the case variable. In that case it is O(1).
Most compilers will optimize the examples in your question to be nearly or even exactly the same. The issue, therefore, is one of readability.
If you have one or two cases, an if statement usually makes sense. If you have many, especially if the code for each case is small, then a switch statement tends to be more economical in terms of code required and can be easier to read.
But, at least up to a point, readability is a matter of personal preference.
Switch is faster than if/else blocks when it can be used. When there are more than 5 entries, it is implemented as a lookup. This provides some information regarding performance: Is "else if" faster than "switch() case"?
I believe it is also more readable in these cases.
For the fewer items there will be no significant performance difference between if statements and switch statement. In switch statement every item is accessed directly, in same time, so the last item will take the same time as the first item. In if statement accessing the last item will take a longer time than the first one because it has to traverse through all the items before it. Anyway the latency will not be noticeable for fewer items as in your example.
Here is a good discussion on this topic. Have a look.
If you have a large number of conditions similar to the ones in the provided examples, then I would recommend a Map.
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"one");
map.put(2,"two");
map.put(3,"three");
map.put(4,"four");
I am not sure about the trade-off. I would imagine it would behave similarly to a switch statement. It would reduce the cyclomatic complexity of your code which should make it more readable.
I currently have this very long if statement:
if (somesTring != null && !"".equals(somestring)
SomeClass.doSomething();
else if (somesTring != null && !"".equals(somestring)
SomeClass.doAnother();
...
else
SomeClass.giveUp();
I think this would execute poorly when the item in question is at the bottom of the decision tree. I'm considering the switch statement, which, according to my coworker, is faster. The sad thing is that the comparisons are performed with strings, which is not supported by the switch statement. Maybe I can get the hashcodes of the strings involved and use that for the switch statement.
I'm also considering a hashmap that contains each of those strings and make decisions with the help of the containsKey method. This, however, involves having to slice up a class that contains a lot of static methods and make each of those methods as a somewhat executable object. By doing this, I can dispose of the very large "if" statement.
Which of the said approaches make more sense with regards to execution time, good practice, and maintainability? Other suggestions are welcome :)
Use the command pattern. You can get rid of the if statements completely
Here is similar question
Long list of if statements in Java
By using command,
execution time - You can verify. Don't think its going to be a big difference.
good practice - Yes
maintainability - Yes, there is a pattern established.
"Use switch instead of if-else, its more readable and has better performance." I have to admit that this was one of my favorite code review comment. Until one fine day, while hacking Apache Sanselan's image format decoding function, I tried optimizing the code based on the same comments and while benchmark there was hardly any difference. I thought about investigating it further. Though found some interesting mail chains, though about posting my finding.
To begin with decided to run some samples on switch and if-else constructs and analyze further.
Wrote three function
1. For if-else - a if-else ladder based on int comparisons
2. For Switch - switch with 21 cases, from 1 to 20
3. For Switch - switch with sparse random values
The reason for choosing two functions for switch was this statement from VM spec "Compilation of switch statements uses the tableswitch and lookupswitch instructions"
Lets see the function codes
if-else
view sourceprint
public static void testIfElse(int jumpLabel) {
if(1 == jumpLabel) {
System.out.println("1");
} else if(2 == jumpLabel) {
System.out.println("2");
} else if(3 == jumpLabel) {
System.out.println("3");
} else if(4 == jumpLabel) {
System.out.println("4");
}
// Removed for simplicity
else {
System.out.println("default");
}
}
Lets see the switch functions
Finite switch version
view sourceprint?
public static void testSwitchFinite(int jumpLable) {
switch (jumpLable) {
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
break;
case 3:
System.out.println("3");
break;
case 4:
System.out.println("4");
break;
case 5:
System.out.println("5");
break;
// Removed other cases for simplicity
default:
System.out.println("default");
break;
}
}
Sparse switch version
view sourceprint?
public static void testSwitchSparse(int jumpLable) {
switch (jumpLable) {
case 100:
System.out.println("1");
break;
case -1:
System.out.println("2");
break;
case 5000:
System.out.println("3");
break;
case -8:
System.out.println("4");
break;
case 1600:
System.out.println("5");
break;
case 250:
System.out.println("250");
break;
// Removed other cases for simplicity
default:
System.out.println("default");
break;
}
}
With the groundwork done, its the benchmarking time. The benchmarking strategy was simple. Run these functions in loop and see the result, with iteration ranging from 100 to 1000.
Lets look at one of the functions
view sourceprint?
public static void testSwitchPerf(int iteration) {
long t1 = System.nanoTime();
for (int i = 0; i < iteration; i++) {
testSwitchFinite(i);
}
long t2 = System.nanoTime();
System.out.println("Time Taken (switch) = "+(t2 - t1)/1000000);
}
Well this was the ground work, after executing the conditions, here is the data
Iteration -> 100 1000
if-else 8 ms 69 ms
switch finite 3 ms 34 ms
switch sparse 7 ms 21 ms
NOTE: There is some difference due to the way data is provided and the sample space doesn't provide precise results. However, since the sample space is same for both, it would serve its purpose
Conclusion
1. There is no significant execution difference between if-else and switch. The difference observed is due to the sample space choosen.
2. If using if-else, its always recommended to put frequently used if condition at the top of if-else ladder
3. The finite switch statement was converted to tableswitch and sparse switch was converted to lookupswitch
would be interested to hear from folks about their experience in relation to this. I would say, i still prefer switch for readability. Henceforth, my review comment shall be modified as "Use switch instead of if-else, its more readable and has better performance