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: