Is this functionality going to be put into a later Java version?
Can someone explain why I can't do this, as in, the technical way Java's switch statement works?
Switch statements with String cases have been implemented in Java SE 7, at least 16 years after they were first requested. A clear reason for the delay was not provided, but it likely had to do with performance.
Implementation in JDK 7
The feature has now been implemented in javac with a "de-sugaring" process; a clean, high-level syntax using String constants in case declarations is expanded at compile-time into more complex code following a pattern. The resulting code uses JVM instructions that have always existed.
A switch with String cases is translated into two switches during compilation. The first maps each string to a unique integer—its position in the original switch. This is done by first switching on the hash code of the label. The corresponding case is an if statement that tests string equality; if there are collisions on the hash, the test is a cascading if-else-if. The second switch mirrors that in the original source code, but substitutes the case labels with their corresponding positions. This two-step process makes it easy to preserve the flow control of the original switch.
Switches in the JVM
For more technical depth on switch, you can refer to the JVM Specification, where the compilation of switch statements is described. In a nutshell, there are two different JVM instructions that can be used for a switch, depending on the sparsity of the constants used by the cases. Both depend on using integer constants for each case to execute efficiently.
If the constants are dense, they are used as an index (after subtracting the lowest value) into a table of instruction pointers—the tableswitch instruction.
If the constants are sparse, a binary search for the correct case is performed—the lookupswitch instruction.
In de-sugaring a switch on String objects, both instructions are likely to be used. The lookupswitch is suitable for the first switch on hash codes to find the original position of the case. The resulting ordinal is a natural fit for a tableswitch.
Both instructions require the integer constants assigned to each case to be sorted at compile time. At runtime, while the O(1) performance of tableswitch generally appears better than the O(log(n)) performance of lookupswitch, it requires some analysis to determine whether the table is dense enough to justify the space–time tradeoff. Bill Venners wrote a great article that covers this in more detail, along with an under-the-hood look at other Java flow control instructions.
Before JDK 7
Prior to JDK 7, enum could approximate a String-based switch. This uses the static valueOf method generated by the compiler on every enum type. For example:
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
If you have a place in your code where you can switch on a String, then it may be better to refactor the String to be an enumeration of the possible values, which you can switch on. Of course, you limit the potential values of Strings you can have to those in the enumeration, which may or may not be desired.
Of course your enumeration could have an entry for 'other', and a fromString(String) method, then you could have
ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
case MILK: lap(); break;
case WATER: sip(); break;
case BEER: quaff(); break;
case OTHER:
default: dance(); break;
}
The following is a complete example based on JeeBee's post, using java enum's instead of using a custom method.
Note that in Java SE 7 and later you can use a String object in the switch statement's expression instead.
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
String current = args[0];
Days currentDay = Days.valueOf(current.toUpperCase());
switch (currentDay) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
System.out.println("boring");
break;
case THURSDAY:
System.out.println("getting better");
case FRIDAY:
case SATURDAY:
case SUNDAY:
System.out.println("much better");
break;
}
}
public enum Days {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
}
Switches based on integers can be optimized to very efficent code. Switches based on other data type can only be compiled to a series of if() statements.
For that reason C & C++ only allow switches on integer types, since it was pointless with other types.
The designers of C# decided that the style was important, even if there was no advantage.
The designers of Java apparently thought like the designers of C.
An example of direct String usage since 1.7 may be shown as well:
public static void main(String[] args) {
switch (args[0]) {
case "Monday":
case "Tuesday":
case "Wednesday":
System.out.println("boring");
break;
case "Thursday":
System.out.println("getting better");
case "Friday":
case "Saturday":
case "Sunday":
System.out.println("much better");
break;
}
}
James Curran succinctly says: "Switches based on integers can be optimized to very efficent code. Switches based on other data type can only be compiled to a series of if() statements. For that reason C & C++ only allow switches on integer types, since it was pointless with other types."
My opinion, and it's only that, is that as soon as you start switching on non-primitives you need to start thinking about "equals" versus "==". Firstly comparing two strings can be a fairly lengthy procedure, adding to the performance problems that are mentioned above. Secondly if there is switching on strings there will be demand for switching on strings ignoring case, switching on strings considering/ignoring locale,switching on strings based on regex.... I would approve of a decision that saved a lot of time for the language developers at the cost of a small amount of time for programmers.
Beside the above good arguments, I will add that lot of people today see switch as an obsolete remainder of procedural past of Java (back to C times).
I don't fully share this opinion, I think switch can have its usefulness in some cases, at least because of its speed, and anyway it is better than some series of cascading numerical else if I saw in some code...
But indeed, it is worth looking at the case where you need a switch, and see if it cannot be replaced by something more OO. For example enums in Java 1.5+, perhaps HashTable or some other collection (sometime I regret we don't have (anonymous) functions as first class citizen, as in Lua — which doesn't have switch — or JavaScript) or even polymorphism.
If you are not using JDK7 or higher, you can use hashCode() to simulate it. Because String.hashCode() usually returns different values for different strings and always returns equal values for equal strings, it is fairly reliable (Different strings can produce the same hash code as #Lii mentioned in a comment, such as "FB" and "Ea") See documentation.
So, the code would look like this:
String s = "<Your String>";
switch(s.hashCode()) {
case "Hello".hashCode(): break;
case "Goodbye".hashCode(): break;
}
That way, you are technically switching on an int.
Alternatively, you could use the following code:
public final class Switch<T> {
private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(0);
public void addCase(T object, Runnable action) {
this.cases.put(object, action);
}
public void SWITCH(T object) {
for (T t : this.cases.keySet()) {
if (object.equals(t)) { // This means that the class works with any object!
this.cases.get(t).run();
break;
}
}
}
}
For years we've been using a(n open source) preprocessor for this.
//#switch(target)
case "foo": code;
//#end
Preprocessed files are named Foo.jpp and get processed into Foo.java with an ant script.
Advantage is it is processed into Java that runs on 1.0 (although typically we only supported back to 1.4). Also it was far easier to do this (lots of string switches) compared to fudging it with enums or other workarounds - code was a lot easier to read, maintain, and understand. IIRC (can't provide statistics or technical reasoning at this point) it was also faster than the natural Java equivalents.
Disadvantages are you aren't editing Java so it's a bit more workflow (edit, process, compile/test) plus an IDE will link back to the Java which is a little convoluted (the switch becomes a series of if/else logic steps) and the switch case order is not maintained.
I wouldn't recommend it for 1.7+ but it's useful if you want to program Java that targets earlier JVMs (since Joe public rarely has the latest installed).
You can get it from SVN or browse the code online. You'll need EBuild to build it as-is.
Other answers have said this was added in Java 7 and given workarounds for earlier versions. This answer tries to answer the "why"
Java was a reaction to the over-complexities of C++. It was designed to be a simple clean language.
String got a little bit of special case handling in the language but it seems clear to me that the designers were trying to keep the amount of special casing and syntactic sugar to a minimum.
switching on strings is fairly complex under the hood since strings are not simple primitive types. It was not a common feature at the time Java was designed and doesn't really fit in well with the minimalist design. Especially as they had decided not to special case == for strings, it would be (and is) a bit strange for case to work where == doesn't.
Between 1.0 and 1.4 the language itself stayed pretty much the same. Most of the enhancements to Java were on the library side.
That all changed with Java 5, the language was substantially extended. Further extensions followed in versions 7 and 8. I expect that this change of attitude was driven by the rise of C#
The technicalities were nicely explained in this answer. I just wanted to add that with Java 12 switch expressions you can do it with the following syntax:
String translation(String cat_language) {
return switch (cat_language) {
case "miau miau" -> "I am to run";
case "miauuuh" -> "I am to sleep";
case "mi...au?" -> "leave me alone";
default -> "eat";
};
}
JEP 354: Switch Expressions (Preview) in JDK-13 and JEP 361: Switch Expressions (Standard) in JDK-14 will extend the switch statement so it can be used as an expression.
Now you can:
directly assign variable from switch expression,
use new form of switch label (case L ->):
The code to the right of a "case L ->" switch label is restricted to be an expression, a block, or (for convenience) a throw statement.
use multiple constants per case, separated by commas,
and also there are no more value breaks:
To yield a value from a switch expression, the break with value statement is dropped in favor of a yield statement.
So the demo from the answers (1, 2) might look like this:
public static void main(String[] args) {
switch (args[0]) {
case "Monday", "Tuesday", "Wednesday" -> System.out.println("boring");
case "Thursday" -> System.out.println("getting better");
case "Friday", "Saturday", "Sunday" -> System.out.println("much better");
}
In Java 11+ it's possible with variables too. The only condition is it must be a constant.
For Example:
final String LEFT = "left";
final String RIGHT = "right";
final String UP = "up";
final String DOWN = "down";
String var = ...;
switch (var) {
case LEFT:
case RIGHT:
case DOWN:
default:
return 0;
}
PS. I've not tried this with earlier jdks. So please update the answer if it's supported there too.
Not very pretty, but here is another way for Java 6 and bellow:
String runFct =
queryType.equals("eq") ? "method1":
queryType.equals("L_L")? "method2":
queryType.equals("L_R")? "method3":
queryType.equals("L_LR")? "method4":
"method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);
Related
I'm trying to make a game and I have a Selection class that holds a string named str in it. I apply the following code to my selection objects every 17 milliseconds.
if(s.Str == "Upgrade") {
}else if(s.Str == "Siege") {
}else if(s.Str == "Recruit") {
}
In other words, these selection objects will do different jobs according to their types(upgrade,siege etc...). I am using str variable elsewhere. my question is that:
Would it be more optimized if I assign the types to an integer when I first create the objects?
if(s.type == 1) {
}else if(s.type == 2) {
}else if(s.type == 3) {
}
This would make me write extra lines of code(Since I have to separate objects by type when I first create) and make the code more difficult to understand, but would there be a difference between comparing integers rather than comparing strings?
If you compare strings >that< way, there is probably no performance difference.
However, that is the WRONG WAY to compare strings. The correct way is to use the equals(Object) method. For example.
if (s.Str.equals("Upgrade")) {
Read this:
How do I compare strings in Java?
I apply the following code to my selection objects every 17 milliseconds.
The time that it will take to test two strings for equality is probably in the order of tens of NANOseconds. So ... basically ... the difference between comparing strings or integers is irrelevant.
This illustrates why premature optimization is a bad thing. You should only optimize code when you know that it is going to be worthwhile to spend your time on it; i.e. when you know there is going to be a pay-off.
So should I optimize after I write and finish all the code? Does 'not doing premature optimization' means that?
No it doesn't exactly mean that. (Well .. not to me anyway.) What it means to me is that you shouldn't optimize until:
you have a working program whose performance you can measure,
you have determined specific (quantifiable) performance criteria,
you have a means of measuring the performance; e.g. an appropriate benchmarks involving real or realistic use-cases, and
you have good a means of identifying the actual performance hotspots.
If you try to optimize before you have the above, you are likely to optimize the wrong parts of the code for the wrong reasons, and your effort (programmer time) is likely to be spent inefficiently.
In your specific case, my gut feeling is that if you followed the recommended process you would discover1 that this String vs int (vs enum) is irrelevant to your game's observable performance2.
But if you want to be more scientific than "gut feeling", you should wait until you have 1 through 4 settled, and then measure to see if the actual performance meets your criteria. Only then should you decide whether or not to optimize.
1 - My prediction assumes that your characterization of the problem is close enough to reality. That is always a risk when people try to identify performance issues "by eye" rather than by measuring.
2 - It is relevant to other things; e.g. code readability and maintainability, but I'm not going to address those in this Answer.
The Answer by Stephen C is correct and wise. But your example code is ripe for a different solution entirely.
Enum
If you want performance, type-safety, easier-to-read code, and want to ensure valid values, use enum objects rather than mere strings or integers.
public enum Action { UPGRADE , SIEGE , RECRUIT }
You can use a switch for the various enum possible objects.
Action action = Action.SIEGE ;
…
switch ( action )
{
case UPGRADE:
doUpgradeStuff() ;
break;
case SIEGE:
doSiegeStuff() ;
break;
case RECRUIT:
doRecruitStuff() ;
break;
default:
doDefaultStuff() ;
break;
}
Using enums this way will get even better in the future. See JEP 406: Pattern Matching for switch (Preview).
See Java Tutorials by Oracle on enums. And for an example, see their tutorial using enums for month, day-of-week, and text style.
See also this Question, linked to others.
Comparing primitive numbers like Integer will be definitely faster compared to String in Java. It will give you faster performance if you are executing it every 17 milliseconds.
Yes there is difference. String is a object and int is a primitive type. when you are doing object == "string" it is matching the address. You need to use equals method to check the exact match.
using bug finder plugin, I found this bugs but does not understand why it was seen as bug in the code. Does anybody know and give me proper explanation regarding these? Thanks.
Source Code - https://drive.google.com/open?id=1gAyHFcdHBShV-9oC5G7GeOtCGf7bXoso;
Patient.java:17 Patient.generatePriority() uses the nextDouble method of Random to generate a random integer; using nextInt is more efficient [Of Concern(18), Normal confidence]
public int generatePriority(){
Random random = new Random();
int n = 5;
return (int)(random.nextDouble()*n);
}
ExaminationRoom.java:25 ExaminationRoom defines equals and uses Object.hashCode() [Of Concern(16), Normal confidence]
public boolean equals(ExaminationRoom room){
if (this.getWaitingPatients().size() == room.getWaitingPatients().size()){
return true;
}
else {
return false;
}
}
ExaminationRoom.java:15 ExaminationRoom defines compareTo(ExaminationRoom) and uses Object.equals() [Of Concern(16), Normal confidence]
// Compares sizes of waiting lists
#Override
public int compareTo(ExaminationRoom o) {
if (this.getWaitingPatients().size() > o.getWaitingPatients().size()){
return 1;
}
else if (this.getWaitingPatients().size() < o.getWaitingPatients().size()){
return -1;
}
return 0;
}
Hospital.java:41 Bad month value of 12 passed to new java.util.GregorianCalendar(int, int, int) in Hospital.initializeHospital() [Scary(7), Normal confidence]
doctors.add(new Doctor("Hermione", "Granger", new GregorianCalendar(1988, 12, 10), Specialty.PSY, room102));
Person.java:29 Return value of String.toLowerCase() ignored in Person.getFullName() [Scariest(3), High confidence]
public String getFullName(){
firstName.toLowerCase();
Character.toUpperCase(firstName.charAt(0));
lastName.toLowerCase();
Character.toUpperCase(lastName.charAt(0));
return firstName + " " + lastName;
}
Don’t create a new Random object each time.
Use random.nextInt(n).
Define a hashCode method in ExaminationRoom.
Having your compareTo method be inconsistent with equals() may or may not be OK.
Use LocalDate instead of GregorianCalendar.
Pick up and use the return values from String.toLowerCase() and Character.toUpperCase().
Consider SpotBugs as a newer alternative to FindBugs.
Details
Random
Creating a new Random object each time you need one gives your poorer pseudo-random numbers with a high risk of numbers being repeated. Declare a static variable holding the Random object outside your method and initialize it in the declaration (Random is thread-safe, so you can safely do that). For drawing a pseudo-random number from 0 through 4 use
int n = 5;
return random.nextInt(n);
It’s not only more efficient (as FindBugs says), I first of all find it much more readable.
hashCode()
#Override
public int hashCode() {
return Objects.hash(getWaitingPatients());
}
compareTo()
The equals method you have shown us seems to contradict FindBugs here. It does look a bit funny, though. Are two waiting rooms considered the same if they have the same number of waiting patients? Please think again. If you end up deciding that they are not equal but should be sorted into the same spot without discrimination, your compareTo method is inconsistent with equals(). If so, please insert a comment stating this fact. If you want FindBugs not to report this as a bug in subsequent analyses, you have two options:
Insert an annotation telling FindBugs to ignore the “bug”.
Create a FindBugs ignore XML file including this point.
I’m sorry I don’t remember the detauls of each, but your search engine should be helpful.
Don’t use GregorianCalendar
The GregorianCalendar class is poorly designed and long outdated. I suggest you evict it from your code and use LocalDate from java.time, the modern Java date and time API, instead.
doctors.add(new Doctor("Hermione", "Granger", LocalDate.of(1988, Month.DECEMBER, 10), Specialty.PSY, room102));
String.toLowerCase()
This was already treated in the other answer. Changing a name to have the first letter in upper case and the rest in lower case is not as simple as it sounds.
firstName.toLowerCase();
Character.toUpperCase(firstName.charAt(0));
The first of these two lines doesn’t modify the string firstName because strings have been designed to be immutable and toLowerCase() to return a new string with all letters in lower case (according to the rules of the default locale of the JVM, confusing). The second line also doesn’t modify any character because Java is call-by-value (look it up), so no method can modify a variable passed as argument. You’re not even passing a variable, but the return value from a different method. Also Character.toUpperCase() returns a new char in lower case.
What you need to do is pick up the values returned from these two method calls, use a substring operation for removing the first letter from the lower-case version of the name and concatenate the upper-case version of that letter with the remainder of the lower-case string. If it’s complicated, I am sure that your search engine can find examples of where and how it is done.
A bit of an aside: You may want to think twice before forcing Dr. Jack McNeil to be written as Mcneil and Dr. Ludwig von Saulsbourg as Von saulsbourg.
SpotBugs
It’s only something I have heard, I haven’t checked myself. The source code of FindBugs has been taken over by a project called SpotBugs. They say that SpotBugs is being developed more actively than FindBugs. So you may consider switching. I am myself a happy SpotBugs user in my daily work.
Links
What issues should be considered when overriding equals and hashCode in Java?
Documentation of Comparable explaining what it means that compareTo() is inconsistent with equals() and how to go about it.
Oracle tutorial: Date Time explaining how to use java.time.
Chicago Hope listing Jack McNeil as Orthopedic Surgeon.
Dr. Jack mentioning Dr. Ludwig von Saulsbourg on the cast.
SpotBugs
First thing to remember about "bug finder" tools, is that they are usually only guidelines. With that said:
The class GregorianCalendar counts months from 0, meaning 0 is January, 11 is December. 12 represents the 13th month which doesn't exist. Since the function expects an int, and you gave it an int, no compiler error is generated, even though this is certainly a bug. This article does a good job explaining the reasons to upgrade, and give examples of how to use the new APIs: https://www.baeldung.com/java-8-date-time-intro
If in doubt, you can always check the documentation. In this case, the class Calendar (which GregorianCalendar extends) delcares a static constant public static final int JANUARY = 0; This confirms that january is indeed 0, but also indicates that we can use this constant in our code. You might find new GregorianCalendar(1988, Calendar.JANUARY, 10) to be a bit more readable.
You may also want to consider switching to the more modern and standard systems used to deal with time. The Java 8 Time libraries are the "new standard", and are definitely worth looking into.
Secondly, Strings are immutable in Java. This means that once a String is created, its value can never be changed. This may be counter to your intuitions, as you may have seen code such as:
String s = "hello";
s = s + " world";
However, this doesn't modify the string s. Instead, s + " world" creates a new String, and assigns it to the variable s.
Similarly, s.toLowerCase() doesn't change what s is, it only generates a new String which you must assign.
You probably want firstName = firstName.toLowerCase();
With your first example, nothing immediately jumps out to me as "bad", but if you look at the messages generated by your tool, they label the first example as "Of Concern", but label the others (e.g. the string.toLowerCase() example) as "Scary"/"Scariest". Although I am not familiar with this tool in particular, I imagine this is indicating more of a "Code Smell", rather than an actual bug.
Perhaps look into Unit Testing, if you want to reassure yourself that your code works.
switch (Category.categorize(input)) {
case MONEY:
amount += input.amount();
state = ADDING_MONEY;
break;
case SHUT_DOWN:
state = TERMINAL;
// why ???
default:
}
these codes come from ,my question is :
why set default,it's nonsense ?? i think it's good to code like this:
switch (Category.categorize(input)) {
case MONEY:
amount += input.amount();
state = ADDING_MONEY;
break;
case SHUT_DOWN:
state = TERMINAL;
// why ???
}
It's not necessary, it is a style thing. In the Google Java style guide it says:
4.8.4.3 The default case is present
Each switch statement includes a default statement group, even if it
contains no code.
In the event you have a different case added that isn't handled in the switch, it would be nice to have a default that did something, whether to throw an exception, do logging, or whatever, to inform someone there was a case that isn't handled. Having a default label makes it more apparent that there is a spot where some code like this needs to go.
It may be Bruce Eckel decided to make his code comply with the Google style guide, and added this as a result. Following agreed-on conventions can be a good thing even if you don't agree with all of them, there is an argument that "form is liberating".
Herbert Schildt in "The Complete Reference" - Chapter 5:
A switch statement is usually more efficient than a set of nested ifs.
When the compiler compiles a switch statement , it inspects each of the case constants and creates a "Jump Table" that it will use for selecting the path of execution depending on the value of the expression. Therefore, if you need to select among a large group of values, a switch statement will run much faster than the equivalent logic coded using a sequence of if-elses. The compiler can do this because it knows that the case constants are all the same type and simply must be compared for equality with the switch expression. The compiler has no such knowledge of a long list of if expressions.
what does he mean by the term "JUMP TABLE"?
Switch differs from if-statements in that, switch can test only for equality whereas if can evaluate any type of boolean expression. If a switch(expression) does not match any of the case constants, that means it goes inside the default case. Isn't it a case of unequality? That makes me think of having no such big difference between the switch and if.
From an extract in the same book, he wrote:
An if-then-else statement can test expressions based on ranges of values or conditions, whereas a switch statement tests expressions based only on a single integer, enumerated value, or String object.
Doesn't that make if more powerful than switch?
I have worked in different projects on Core Java, but never felt the need to make use of the switch statement, and also not seen other associates make use of it. Not even a single time. How come a much more powerful switch control statement fails to land itself ahead of if in terms of usability?
1) The compiler creates a table with a row for every case statement, where the case condition is the key and the block of code is the value. When the switch statement is executed in your code, the block of the case can directly be accessed by the key of the case block. Where in nested if blocks you need to check and execute every condition before your will find the right one where your enter the conditional block of code.
Think of it like a hashtable (for switch case) compared to a linear linked list (for nested if statements) and compare the time to look up a specific value. The lookup time of a table is O(1) (just get it in one operation) where the lookup time in the linked list is O(n) (look at every item until you get the right one).
3) yes, an if statement can be used more flexible and express more, but in the case you want to switch between different codeblocks for a list of different values a switch block is executed faster and more readable.
4) you never need to use a switch because nested ifs can express the same, but in some cases it is more elegant. But because of the rare time it is good to use, programmers forget about it and don't use it even in the spots where it would fit better.
2) extract the difference out of the answers to the other points.
I use switch all the time, if you aren't using it you're doing Java wrong.
A jump table can be thought of like an array of locations in the code. For example imagine the following:
case 0:
// do stuff
break;
case 1:
// do stuff 2
break;
case 3:
// do stuff 3
break;
Then you have a jump table just containing
codeLocations[] = { do stuff, do stuff 2, do stuff 3 }.
Then to find the correct bit of code for case 1 Java just needs to goto codeLocations[1]. No actual integer comparisons are needed.
Obviously the actual implementation is rather more complicated than this as it needs to handle much more varied cases but it gives the idea.
Neither if nor switch are more powerful than each other. They are both tools. You're asking whether a hammer is more powerful than a screwdriver. Yes a hammer will knock more things into the wood, but a screwdriver will do a better job if you are working with screws :)
To expand on the "doing java wrong" thing:
Switch is a tool in the Java language, one that has been added for a reason.
You could apply the exact same question to for loops. Everything you can do with a for loop you can also do with a while loop (although it will likely involve writing quite a bit more code). Try it sometime.
This doesn't make while "more powerful" or even "more useful" than for. They are different tools with different strengths and weaknesses.
switch and if are the same as for and while. They do similar jobs and in a lot of places you could use either. However there are clear examples where one is preferable to the other.
If you are programming using a language while ignoring one of the fundamental tools in that languages toolkit then you are using that language wrong. This is not some obscure feature that hardly ever gets used, this is one of the primary flow control statements in the language.
If a carpenter only ever uses a hammer and never uses a screwdriver... they are doing it wrong. Equally if a programmer never uses a significant language feature...they are doing it wrong.
A proverb applies here "If the only tool you have is a hammer, everything looks like a nail".
1: A jump table is a table of jump targets. A jump target is an address in the code that corresponds to the code belonging to a case in the switch. If your switch cases are, for a simple example, 0, 1, ... N, then the N + 1 targets would be at index 0, 1, ... N of the table, respectively. You just calculate the value you're switching on an then look up the target by indexing into the table, not by comparing N + 1 times. That's the efficiency.
2: default is a case of inequality, yes, but very limited, as you note. Its the case for "not equal to any other of the cases" but it can't be "less than a given value" or "greater than..."
3: Yes, if is more powerful. However, switch is more efficient, sometimes. Efficiency != power.
4: Power != efficiency. But, to answer the implied question: the more efficient switch statement is not used because your colleagues have not used it. You have to ask them why. Maybe it's a matter of taste, or of ignorance of the advantages of switch. The greater efficiency of a switch statement is almost always going to be unnoticeable, unless you are in a very performance-sensitive context, so people are generally unaware of it. If performance is not paramount, then readability is. In some cases a developer might consider a switch statement and decide that a series of if statements is easier to read and understand.
1) In a switch case, a jump table maps a position in the code segment to a particular case. For instance, considering this snippet:
int a;
// a is manipulated here
switch (a) {
case 1: // address 0
// ...
break;
case 2: // address 1
// ...
break;
case 3: // address 2
// ...
break;
default: // other cases
// ...
break;
}
There would be a jump table mapping a=1 to the effective code address marked by address 0, a=2 to address 1, and so on. This can become more apparent when observing the resulting bytecode. With this approach, the number of conditional jumps in the bytecode becomes much smaller than in the case of if-else statements.
2) It's true that if-else statements can test of unequality, but achieving the same behavior of a switch-case statement involves chaining if-else statements. Here is an equivalent version of the above in terms of behavior.
if (a==1) {
// ...
} else if (a==2) {
// ...
} else if (a==3) {
// ...
} else {
// for any other cases
}
It could be a matter of personal opinion at some point, but a switch-case statement is more readable in this particular case.
But naturally, if-else statements are still indispensable, and you are expected to use the right tool for the job. If-else is more versatile than switch-case, but the latter may produce nicer code in some places. Hopefully, this answers the 3rd and 4th part of your question.
In layman's terms difference is negligible.
Both have their pros and cons. In most cases you would never use switch - instead you would use hashmap with actions or some other construct that has better readability.
I would reccomend you read Efficient Java or https://code.google.com/p/guava-libraries/wiki/GuavaExplained
In c# you could write this like :
class MyClass
{
static Dictionary<string, Func<MyClass, string, bool>> commands
= new Dictionary<string, Func<MyClass, string, bool>>
{
{ "Foo", (#this, x) => #this.Foo(x) },
{ "Bar", (#this, y) => #this.Bar(y) }
};
public bool Execute(string command, string value)
{
return commands[command](this, value);
}
public bool Foo(string x)
{
return x.Length > 3;
}
public bool Bar(string x)
{
return x == "";
}
}
After that you could have
var item = new MyClass();
item.Execute("Foo","ooF");
It's the same syntax in a way too many languages:
switch (someValue) {
case OPTION_ONE:
case OPTION_LIKE_ONE:
case OPTION_ONE_SIMILAR:
doSomeStuff1();
break; // EXIT the switch
case OPTION_TWO_WITH_PRE_ACTION:
doPreActionStuff2();
// the default is to CONTINUE to next case
case OPTION_TWO:
doSomeStuff2();
break; // EXIT the switch
case OPTION_THREE:
doSomeStuff3();
break; // EXIT the switch
}
Now, all you know that break statements are required, because the switch will continue to the next case when break statement is missing. We have an example of that with OPTION_LIKE_ONE, OPTION_ONE_SIMILAR and OPTION_TWO_WITH_PRE_ACTION. The problem is that we only need this "skip to next case" very very very rarely. And very often we put break at the end of case.
It's very easy for a beginner to forget about it. And one of my C teachers even explained it to us as if it was a bug in C language (don't want to talk about it :)
I would like to ask if there are any other languages that I don't know of (or forgot about) that handle switch/case like this:
switch (someValue) {
case OPTION_ONE: continue; // CONTINUE to next case
case OPTION_LIKE_ONE: continue; // CONTINUE to next case
case OPTION_ONE_SIMILAR:
doSomeStuff1();
// the default is to EXIT the switch
case OPTION_TWO_WITH_PRE_ACTION:
doPreActionStuff2();
continue; // CONTINUE to next case
case OPTION_TWO:
doSomeStuff2();
// the default is to EXIT the switch
case OPTION_THREE:
doSomeStuff3();
// the default is to EXIT the switch
}
The second question: is there any historical meaning to why we have the current break approach in C? Maybe continue to next case was used far more often than we use it these days ?
From this article, I can enumerate some languages that don't require a break-like statement:
Ada (no fallthrough)
Eiffel (no fallthrough)
Pascal (no fallthrough)
Go - fallthrough
Perl - continue
Ruby (no fallthrough)
VB, VBA, VBS, VB.NET (no fallthrough)
To be continued by someone else...
Your second question is pretty interesting. Assuming only C, I believe this decision keeps the language cohesive. Since break is a jump, it must be explicitly written.
Scala pattern matching I think is a huge improvement in these cases. :)
object MatchTest2 extends Application {
def matchTest(x: Any): Any = x match {
case 1 => "one"
case "two" => 2
case y: Int => "scala.Int"
}
println(matchTest("two"))
}
Sample from scala-lang.org
And VB .NET handles it a little more like how you expect it should work.
Select Case i
Case 1 to 3
DoStuff(i)
Case 4,5,6
DoStuffDifferently(i)
Case Is >= 7
DoStuffDifferentlyRedux(i)
Case Else
DoStuffNegativeNumberOrZero(i)
End Select
There is no fall through at all, without possibly using a Goto
Here is the answer:
http://en.wikipedia.org/wiki/Switch_statement
It's called fall-through statement (continue in the example) and it exists in the following languages:
Go, Perl, C#
In C# it won't compile without break or goto case statement (except when there's no pre-action).
PASCAL doesn't have fall-through
I think the answer to your question of why it is this way centers around two behaviors, both having to do with the generated assembly code from the C source.
The first is that in assembly, the current instruction is executed, and unless there is a jump or some other flow control instruction, the instruction at the next address will be executed. Performing a naive compile of the switch statement to assembly would generate code that would just start executing the first instruction, which would be to see if there was a matching condition...
The second related reason is the notion of a branch table or jump list. Basically the compiler can take what it knows about your value, and create some extremely efficient machine code for the same thing. Take for example a simple function like atoi that converts a string representation of a number and returns it in integer form. Simplifying things way down to support just a single digit, you could write some code similar to this:
int atoi(char c) {
switch (c) {
case '0': return 0;
case '1': return 1;
// ....
}
}
The naive compiler would perhaps just convert that to a series of if/then blocks, meaning a substantial amount of CPU cycles would be taken for the number 9, while 0 returns almost immediately. Using a branch table the compiler could emit some [psuedo] assembly that would immediately "jump" to the correct return clause:
0x1000 # stick value of c in a register
0x1004 # jump to address c + calculated offset
# example '0' would be 0x30, the offset in for this sample
# would always be 0x0FD8... thus 0x30 + 0x0FD8 = 0x1008
0x1008 # return 0
Apology: my assembly and C skills are quite rusty. I hope this helps clarify things.
0x
Hey, don't forget COBOL's EVALUATE:
EVALUATE MENU-INPUT
WHEN "0" PERFORM INIT-PROC
WHEN "1" THRU "9" PERFORM PROCESS-PROC
WHEN "R" PERFORM READ-PARMS
WHEN "X" PERFORM CLEANUP-PROC
WHEN OTHER PERFORM ERROR-PROC
END-EVALUATE.
Ada doesn't have fallthrough, and requires that all values are explicitly handled, or a "others" clause added to handle the rest.
SQL CASE statement also does not fallthrough.
XSLT has which does not fallthrough.
It seems to be C and derived languages that have the fallthrough. It's quite insidious, and the only real use I've seen is implementing duff's device.
http://www.adaic.org/whyada/intro4.html
Python doesn't have one at all.
Took some getting used to but I have some horrendous memories hunting through massive switch blocks back in my C# days. I'm much happier without it.
Although not exactly what you asked for, Groovy has a very powerful switch statement
The OP talks about "fall through", but very seldom have I ever been bit by that.
Many many times, however I have been bit by designs that are non-extensible. To wit, "switch (kbHit)" statements, with a few hundred keys in there, that are a maintenance nightmare, and a frequent location for "god methods", and giant piles of spaghetti-code.
Using switch is often a sign of poor object oriented programming. As another person answered, "2 uses of Switch in 48 source files", in one of his application, shows a programmer who does not rely heavily on this construct. From his metric, I surmise that he is probably at least a good structured programmer, and probably understands OOP/OOD as well.
OOP (not necessarily only C++) programmers, and even pure C users who do not have an object description technique forced upon them, could implement an "inversion of control" container that publishes a "key was hit" and allows subscribers to plug in their handlers for "on keyboard code x". This can make reading your code much easier.
Pure speculation, but:
I occasionally write C or Java in which I say something like:
switch (tranCode)
{
case 'A':
case 'D':
case 'R':
processCredit();
break;
case 'B':
case 'G':
processDebit();
break;
default:
processSpecial();
}
That is, I deliberately use fall-thru to let several values fire the same operation.
I wonder if this is what the inventors of C were thinking of when they created the SWITCH statement, that this would be the normal usage.
Tcl doesn't automatically fall through.
In object oriented languages you use the Chain of Responsibility pattern. How that is implemented can vary. What you are describing in your example, is mixing a state machine with behavior by abusing a switch statement. For your particular example a Chain of Responsibility pattern with the parameter that the chain evaluates being a State pattern, that mutates as it goes down the chain, would be the appropriate implementation.
languages are too much and I can't answer for sure that there's not such a language, provided it is a "derivative" of C in syntax, because other languages using different syntax and where the case does not "continue" naturally exist, e.g. Fortran. I don't know languages that uses an explicit "continue" to continue to the following case.
I believe it is historical reason due to the way such a case could be programmed at a "low level". Moreover the syntactical aspect of the case is that of a label, and break works like in loops, so you can imagine an equivalent like this:
if ( case == 1 ) goto lab1;
if ( case == 2 ) goto lab2;
if ( case == 3 ) goto lab3;
//...
default:
// default
goto switch_end;
lab1:
// do things
goto switch_end; // if break is present
lab2:
// do things, and follow to lab3
lab3:
// lab3 stuffs
goto switch_end;
//
...
switch_end: // past all labS.
More languages without a fallthough:
XSLT
JSTL
Algol
PL/1
Lima does it like this:
if someValue == ?1 ::
OPTION_ONE: fall
OPTION_LIKE_ONE: fall
OPTION_ONE_SIMILAR:
doSomeStuff1[]
;; the default is to EXIT the switch
OPTION_TWO_WITH_PRE_ACTION:
doPreActionStuff2[]
fall ;; fall through to the next case
OPTION_TWO:
doSomeStuff2[]
OPTION_THREE:
doSomeStuff3[]