Is a switch statement the fastest way to implement operator interpretation in Java
Is a switch statement the fastest way to implement operator interpretation in Java
public boolean accept(final int op, int x, int val) {
switch (op) {
case OP_EQUAL:
return x == val;
case OP_BIGGER:
return x > val;
case OP_SMALLER:
return x < val;
default:
return true;
}
}
In this simple example, obviously yes. Now imagine you have 1000 operators. would it still be faster than a class hierarchy? Is there a threshold when a class hierarchy becomes more efficient in speed than a switch statement? (in memory obviou开发者_JS百科sly not)
abstract class Op {
abstract public boolean accept(int x, int val);
}
And then one class per operator.
EDIT: Sorry, I should have been more specific by the look of the answers. The Operator is totally unknown and I'm using JDk 1.4. No choice. No enums. No Closures. :( The operator is chosen by the user among many many many choices. For simplicity sake, Imagine a GUI List with 1000 operations, when user selects one, op code of the switch statement is chosen. Using a class hierarchy, user would select a class. I'm asking this question because someone must have tested it before. I don't feel like creating 1000 classes and 1000 bogus op codes to test it. If nobody has done it. I will test it and report the results, if they may have any meaning at all.
EDIT:
Okay, since you have to use JDK 1.4, my original answer is a no-go (left below for reference). I would guess that the switch
is not as fast as the abstract class-based solution when you're just looking at the apply(which,a,b)
vs which.apply(a,b)
call. You'll just have to test that.
However, when testing, you may also want to consider start-up time, memory footprint, etc.
ORIGINAL:
public enum OPERATION {
// ...operators+implementation, e.g.:
GREATER_THAN { public boolean apply(int a, int b) { return a > b; } };
public abstract boolean apply(int a, int b);
}
usage:
OPERATION x = //..however you figure out which
boolean result = x.apply(a,b);
this is one of the case uses in Effective Java for enums. It works exactly like the switch
, only less confusing.
Because of the way a switch statement is usually implemented in a jvm, with a lookup table, it is likely it is going to be faster, with a small or big number of operators. This is just guessing; to have a definite answer you need to benchmark it on the system it is intended to run.
But, that is just a micro-optimization which you shouldn't care about unless profiling shows that it could really make a difference. Using integers instead of a specific class (or enum) makes code less readable. A huge switch statement with 1000 cases is a sign of a bad design. And that will have an influence on the code that is using the operators; less readable, more bugs, harder to refactor,...
And to get back to performance, which seems to be the goal here. In hard to read, badly designed code, the changes required for macro-optimizations become harder. And those optimizations are usually a lot more important than micro-optimizations like this switch.
I do not know what is fastest, nor do I think there are any guarantees. Optimization of code is very much dependent on both compiler and runtime.
I do think that it's hard to beat a switch statement. Due to the limitations Java puts on the types which can be switched it can fairly easily be compiled to a lookup table, which is about the fastest access you can get.
Use a table-driven method, as a previous poster pointed out you may use the operator as the index of an array. The value stored in the array could be an instance of a class that performs the comparison. The array can be initialized statically, or better on-demand (lazy loading pattern).
e.g.
// Interface and classes
interface Operator {
boolean operate(int x, int y);
}
class EqualsOperator implements Operator {
boolean operate(int x, int y){
return x==y;
}
}
class NotEqualsOperator implements Operator {
boolean operate(int x, int y){
return x=!y;
}
}
...
// Static initialization
Operator[] operators = new Operator[n];
operator[0] = new EqualsOperator();
operator[1] = new NotEqualsOperator();
...
// Switch
public boolean accept(final int op, int x, int val) {
operator[op].operate(x,val);
}
If the calling method already has to decide which operator value to use and call accept(), then the fastest thing would be to do the comparisons inline in that same calling method.
Alternatively, use three methods (or strategies):
public boolean acceptGreater(int x, int val) {
return x > val;
}
public boolean acceptLess(int x, int val) {
return x < val;
}
public boolean acceptEquals(int x, int val) {
return x == val;
}
I wouldn't look at this purely from a raw performance point of view, but I'd evaluate this as a refactoring candidate, see c2:Refactor Mercilessly. I liked the answer given to code resuability:
- If you repeat it once, copy it.
- If you repeat it twice, refactor it.
I'd identify the adding of multiple case statements as repetition, and then I'd refactor to implement the Strategy Pattern.
I'd name the operator classes with a strategy suffix, and implement the execute method.
I've always found that the java switch statement is not as powerful as I need. In his last release lambdaj implements it with a smart use of closure and Hamcrest matcher.
精彩评论