Using Enum's ordinal value in switch-case statement
This is how is done, provided you have a serialized ordinal somehow, somewhere. Usual way to persist an enum is by its name, not ordinal, though. Also you should not use ordinal in normal circumstances unless trying to implement something like EnumMap/Set. Of course, the enum can be just a port from C alike stuff and dealing with the inevitable int, needs a transform to the Enum object.
Just use Enum.values()
to obtain an array ordered by ordinal()
, since the array is cloned each time, keeping a ref towards is ok.
enum E{
A, B, C...
}
final static E[] vals = E.values();//copy the values(), calling values() clones the array
boolean f(int variable){
switch(vals[variable]){
case A:
...
case B:
...
//break;
default:
...
}
}
Just noticed you need only true and false, that's a Set type of behavior. You can use java.util.EnumSet or a simple long
, if feeling brave (and not having more than 64 enum constants). for example:
private static <E extends Enum> long ord(E e){
return 1L<<e.ordinal();
}
static final long positiveSet = ord(E.A)+ord(E.B);
boolean f(int ordinal){
return 0!=(positiveSet&(1L<<ordinal));
}
First of all, you should not rely on the ordinal that much. If possible make your variable a String
(and transform to enum
using Enum.valueOf(string)
or at best make it enum
.
If you really can't, then use enum.values()[ordinal]
.Then use the enum in the switch.
The answer targets @Riaan comment on constant vs method enums and performance reasons and it doesn't directly answers the OP question, so it can be considered noise I suppose. However, I believe it's an important matter to understand how the inner workings.
I took the benchmark off his example and improved it to remove the garbage collection and string creation that takes over 90% of the execution time. Added warm up phase to ensure hotspot actually compiles the methods.
There is some more, the benchmark is effectively callsite test. The optimization for callsites are quite different for 1, for 2 for few more and for more-more. A callsite is an invocation of abstract (or just overridden) method.
Below is the test with 6 enums constants:
package t1;
public class ZEnums {
public enum MyEnum {
A { boolean getBooleanValue(){ return true; }},
B { boolean getBooleanValue(){ return true; }},
C { boolean getBooleanValue(){ return false; }},
D { boolean getBooleanValue(){ return false; }},
E { boolean getBooleanValue(){ return false; }},
F { boolean getBooleanValue(){ return false; }},
;
abstract boolean getBooleanValue();
}
public enum MyEnumAlt {
A (true),
B (true),
C (false),
D (false),
E (false),
F (false),
;
private final boolean isTrue;
MyEnumAlt( boolean isTrue){ this.isTrue = isTrue; }
boolean getBooleanValue(){ return isTrue; };
}
public static void main(String[] args) {
log("Warming up...");
//10k iterations won't do since not all paths for MyEnum are invoked 10k (default) times to warrant compilations
long warmum = testEnum(100000 )+ testAlt(100000)+testEnum(100000 )+ testAlt(100000);
log("Warm up: %d", warmum);
//no info from +XX:+PrintCompilation below this one, or the test is invalid
testMain();
}
public static void testMain() {
int iterations = (int)4e7;
log("Testing %d iterations%n", iterations);
log("====");
log("Testing with Overridden method...");
System.gc();
{
long start = System.currentTimeMillis();
long len = 0;
len = testEnum(iterations);
long time = System.currentTimeMillis()-start;
log("Overridden method version took %dms, length: %d ", time, len);
}
////////////
System.gc();
{
log("Testing with Constant in c-tor... ");
long start = System.currentTimeMillis();
long len = testAlt(iterations);
long time = System.currentTimeMillis()-start;
log("Constant in c-tor version took %dms, length: %d ", time, len);
}
}
private static long testEnum(int iterations) {
long len = 0;
for(int i=0; i<iterations; i++){
MyEnum tmpEnum = MyEnum.A;
if(i%3==0){ tmpEnum = MyEnum.A;
}else if(i%4==0){ tmpEnum = MyEnum.B;
}else if(i%5==0){ tmpEnum = MyEnum.C;
}else if(i%6==0){ tmpEnum = MyEnum.D;
}else if(i%6==0){ tmpEnum = MyEnum.E;
}else{ tmpEnum = MyEnum.F;
}
String tmp = tmpEnum.getBooleanValue()?"XXX":"ABCDE";
len+=tmp.length();
}
return len;
}
private static long testAlt(int iterations) {
long len =0;
for(int i=0; i<iterations; i++){
MyEnumAlt tmpEnum = MyEnumAlt.A;
if(i%3==0){ tmpEnum = MyEnumAlt.A;
}else if(i%4==0){ tmpEnum = MyEnumAlt.B;
}else if(i%5==0){ tmpEnum = MyEnumAlt.C;
}else if(i%6==0){ tmpEnum = MyEnumAlt.D;
}else if(i%6==0){ tmpEnum = MyEnumAlt.E;
}else{ tmpEnum = MyEnumAlt.F;
}
String tmp = tmpEnum.getBooleanValue()?"XXX":"ABCDE";
len+=tmp.length();
}
return len;
}
static void log(String msg, Object... params){
String s = params.length>0?String.format(msg, params):msg;
System.out.printf("%tH:%<tM:%<tS.%<tL %s%n", new Long(System.currentTimeMillis()), s);
}
}
21:08:46.685 Warming up... 148 1% t1.ZEnums::testEnum @ 7 (125 bytes) 150 1 t1.ZEnums$MyEnum$6::getBooleanValue (2 bytes) 152 2 t1.ZEnums$MyEnum$1::getBooleanValue (2 bytes) 154 3 t1.ZEnums$MyEnum$2::getBooleanValue (2 bytes) 155 4 t1.ZEnums$MyEnum$3::getBooleanValue (2 bytes) 158 2% t1.ZEnums::testAlt @ 7 (125 bytes) 162 5 t1.ZEnums::testEnum (125 bytes) 164 6 t1.ZEnums::testAlt (125 bytes) 21:08:46.716 Warm up: 1600000 21:08:46.716 Testing 40000000 iterations 21:08:46.716 ==== 21:08:46.716 Testing with Overridden method... 21:08:47.513 Overridden method version took 781ms, length: 160000000 21:08:47.513 Testing with Constant in c-tor... 21:08:48.138 Constant in c-tor version took 625ms, length: 160000000
The code was run w/ -server -XX:+PrintCompilation
options.
The difference ain't huge, of course. However that's not the interesting issue. If you test the version with 2 enum constants though, the result can be significantly different. For 2 call sites the compiler generates the code by inlinining the method in question. In the test above that would remove entire the call to booleanValue and can even make execute the test in O(1).
The funniest part however is going from 2 to 3 enum constants when the compiler starts using inline caches and then the constant, and WOW magic the everything changes.
The bottom line is: proper benchmark is truly hard and involves some knowledge how the JIT compiles, when the GC might be an issue (either remove it or embrace it) and so on.
Links:
- Benchmakrs on JavaOne
- How NOT To Write A Microbenchmark