This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
Is "else if" faster than "switch() case"?
What is the relative performance difference of if/else versus switch statement in Java?
I know that case statements can be implemented with jump tables. Does this make them more efficient than if statements?
Is this just micro-optimization that should be avoided?
I think the main thing is to write the code as clearly as possible. Micro-optimizations like this shouldn't be the focus.
For example, if you have something like this:
if (age == 10) {
// ...
} else if (age == 20) {
// ...
} else if (age == 30) {
// ...
} else if (age == 40) {
// ...
}
Then it's clearer to use a switch statement:
switch (age) {
case 10:
// ...
break;
case 20:
// ...
break;
case 30:
// ...
break;
case 40:
// ...
break;
}
Again, I would focus on making the code easiest to read and maintain rather than nano-second level efficiency gains.
Any compiler will make the jump table if it can verify that the values are reasonably compact. (I doubt if they are in this case, being multiples of 10.)
This is a micro-optimization. Micro-optimization makes sense only if you know that it does. Typically, there are larger "fish to fry" elsewhere, in the form of function calls that could be done without. However, if you have already tuned the daylights out of this code, and your profiling shows that a good fraction (like 10% or more) of time is going into these IF statements (and not to their contents) then it helps. This can happen, for example, in a byte-code interpreter.
Added: Another reason I like to use switch is, even if it doesn't make a jump table - when stepping through the code in a debugger, it goes directly to the proper case, rather than making me step through a lot of false if statements. Makes it easier to debug.
If you had a very large chain of if else statements, then, yes, you might feel the difference. But it is very unrealistic that you'll ever write such a long ifelse chain. And if even you did, it's still very unlikely that that is where your performance bottleneck would be.
Write your code to be readable first, and let yourself be guided by a profiler when the need for performance optimization arises.
Probably is doen't matter. Bytecode is just a "transport format" to the JVM. What happens insite the JVM is very different from the bytecode representation. (Example: Bytecode doesn't offer float operations, so float +-*/% float is done as double operations and then the result is casted back to float. Same is true for byte/short, they are converted to int and then back.) But for switch they are two bytecode formats, one already with a jump table. But honestly: I would choose a format which is best for you and the reader of your program. The JVM will do the rest. If you are too smart you JVM maybe don't get your point and in the end the program is slower.
"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" D. Knuth
Yes
No, it is part of your program design. But you should consider whether an over-ridable method mightn't be an even better solution, with a family of types.
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.
Many code analysis tools like sonar throw errors for switch case if used instead of multiple is-else for cyclomatic complexity . Is there a number which should be a threshold of when one should use if else and when one should use switch case ?
Say if its a case of 50 , to reduce cyclomatic complexity one should use switch case ? Would be good if you could support your answer with an example ?
To answer the question with another question.
Would you rather read:
if(cond1)..
else if(cond2)...
else if(cond50)...
else ...
or
value = map.get(key)
If there are so many possible cases, than neither the switch or if seem appropriate and should therefore be replaced with key-value structure or state machine pattern - if branching is really complicated.
You can find an example implementation here:
fsm
As to when to use if or switch, keep in mind that switch cannot accept the following types:
long, double, float, boolean so when you have such an input, switch is obviously not a choice.
I wouldn't use number as a metric to use switch or if statement. There is one exception however, if an input can have only 2 possible values it is better to use if. Even Sonar should suggest you this.
That said, switch is usually more readable than the series of if statements. As an important decision factor, other than readability, within switch statement there can be groups of values which might require same execution path. It is basically safer version of the goto. Unlike if, where each outcome is usually bound to specific execution path.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Advantage of switch over if-else statement
Why Switch/Case and not If/Else If?
I am currently learning "switch case" in school and am wondering what's the point of learning it when "if else" already exists. "if else" is basically another way of doing "switch case".
Please correct me if i am wrong.
It kinda nostalgic to heard it. Both of them actually 'looked' the same. But is a little bit different when the codes executed.
Firstly, 'switch-case' is about comparing value-only. But 'if-else' could process a boolean expression (which would support much more complex clauses)
If you use general 'if-else' when you have found what you are actually searching for, the process will still run until it has finished processing the last if (but actually it could use jump-technique to have similar mechanism like 'switch-case'.)
It won't happen if you use 'switch-case' because once the value you're searching for has been found, it will break and won't continue to the next case. Also, 'switch-case' is faster-to-process than if else because it only compares defined values (not expression). And 'switch-case' also has a good formatting structure (it's simple, compact, readable and clean).
The more tools you have the better. Flat out the best statement of why you should know both... however a more detailed example -
A switch statement works on a single type of variable of the construct:
variable == value
So for example in C if you were trying to compare something to a few different strings in order to make a decision, you can't do that with a switch. In this case you need to know about the if/else constructs.
However if you have a large number of sequential checks:
var == 1 or
var == 2 or
var == 3 etc
The compiler may take your switch statement and convert it to a jump table, which would end up being faster than a large number of comparisons that an if/else list would be.
You should learn the switch construct because it is a useful tool provided by the C language.
It is not the same as if-else blocks.
In the comments section of your question, there are links to existing StackOverflow answers explaining what the differences are.
Each construct has its strengths and weaknesses, and over time you will learn when it is appropriate to choose one over the other.
You should learn both. While it is technically possible to implement any if / else sequence with a switch and vice versa, it would be extremely bad practice to do this ... in most cases.
So you need to learn the two constructs, understand their strengths and weaknesses, and learn to use your judgement as to when it is appropriate to use each one.
And the mere fact that C and C++ and Java (and C# and Pascal and many other languages) all support switch statements should tell you something about its usefulness ...
Difference between switch-case and if-else constructs:
Switch-case switches on values only, it does not evaluates boolean expressions.
Switch-case offers execution of next cases below it automatically if you don't use break after your case block. This feature is sometimes useful for writing complex code, like "Telephone Dial Plan"
Switch-case are more elegant compared to if-else when the number of comparisons are huge, like in displaying "Menu", etc.