Using the Enum ordinal in a switch-case statement

For my project, I use enumerations, and I need to implement a switch-case statement where the sequence numbers of the values ​​of a specific Enum are checked, for example as follows:

switch ( variable ) { case MyEnum.A.ordinal(): return true; case MyEnum.B.ordinal(): return true; default: return false; } 

Note: return values ​​are just an example.

Unfortunately, Eclipse (I use 1.6 JDK) gives my compilation error "case expressions must be constant expressions". What should I do? Is there any other method than the static lookup table described here: Convert from enum ordinal to enum type ?

+6
source share
6 answers

Here's how to do it if you have a serialized serial number somewhere. The usual way to save an enumeration is its name, not the ordinary one. Also, you should not use a serial number under normal conditions unless you are trying to implement something like EnumMap / Set. Of course, an enumeration might just be a port from C, etc. And to deal with the inevitable int, it needs to be converted to an Enum object.

Just use Enum.values() to get the array ordered with ordinal() , as the cloning of the array each time, keeping ref in order.

 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 that you only need true and false, which is a type of type Set. You can use java.util.EnumSet or a simple long if you feel brave (and not having more than 64 enumeration constants). eg:

 private static <E extends Enum> long ord(E e){ return 1L<<e.ordinal(); } static final long positiveSet = ord(EA)+ord(EB); boolean f(int ordinal){ return 0!=(positiveSet&(1L<<ordinal)); } 
+15
source

First of all, you should not rely so heavily on the serial number. If possible, make your variable a String (and convert to enum with Enum.valueOf(string) or at best make it enum .

If you really can't, use enum.values()[ordinal] . Then use the enumeration in the switch.

+4
source

just use enumeration constants:

 MyEnum variable; ... switch ( variable ) { case A: return true; case B: return true; default: return false; } 

Assuming something like:

 public enum MyEnum { A, B } 

but beware of a NullPointerException (if variable is null )

+1
source

What you are asking for is possible: If you need a switch in the enumeration method itself:

 switch ( this ) { case A: return true; case B: return true; default: return false; } 

And in another class:

 switch ( variable ) //Variable of type myEnum { case A: return true; case B: return true; default: return false; } 

It is easy to forget to update switch statements if you add another enumeration, so it is best to introduce such methods in this enumeration and use the default implementation:

 public enum MyEnum A { boolean getBooleanValue(){ return true; }, B { boolean getBooleanValue(){ return true; }, C { boolean getBooleanValue(){ return false; }; abstract boolean getBooleanValue(); } 

That way, if you ever add a new enum value, the compiler will remind you to declare the getBooleanValue method, and you just use A.getBooleanValue(); where you need it.

As noted in the comments, there is another option:

 public enum MyEnumAlt { A (true), B (true), C (false); private final boolean isTrue; MyEnumAlt( boolean isTrue){ this.isTrue = isTrue; } boolean getBooleanValue(){ return isTrue; }; } 

This is a matter of preference and will vary depending on the specific situation. If you simply return a value for each enum, the constructor version is believable, but I find it less readable. Concern about this best result is unfounded, as you can see by testing it:

 public void testMain() { System.out.println("Testing with constructor: "); long start = System.currentTimeMillis(); for(int i=0; i<1000*1000; i++){ MyEnum tmpEnum = null; if(i%3==0){ tmpEnum = MyEnum.A; }else if(i%4==0){ tmpEnum = MyEnum.B; }else{ tmpEnum = MyEnum.C; } String tmp = Integer.toString(i)+" "+tmpEnum.getBooleanValue(); } long time = System.currentTimeMillis()-start; System.out.println("Constructor version took "+time); System.out.println("Testing with Constant specific method implementation: "); long start2 = System.currentTimeMillis(); for(int i=0; i<1000*1000; i++){ MyEnumAlt tmpEnum2 = null; if(i%3==0){ tmpEnum2 = MyEnumAlt.A; }else if(i%4==0){ tmpEnum2 = MyEnumAlt.B; }else{ tmpEnum2 = MyEnumAlt.C; } String tmp2 = Integer.toString(i)+" "+tmpEnum2.getBooleanValue(); } long time2 = System.currentTimeMillis()-start2; System.out.println("Constant specific method version took "+time2); } 
+1
source

The answer points to @Riaan's comment on the vs constant and enumeration of performance, and it does not directly answer the OP question, so it can be considered a hindrance, I suppose. However, I find it important to understand how internal work is going on.

I removed the sample from his example and improved it to remove garbage collection and create a string that takes up more than 90% of the execution time. A warm-up phase has been added to ensure that the access point does compile methods.

There is one more thing, the standard is really a test on sites. The optimization for callites is very different for 1, for 2 for a few more and for more or more. A call is a call to an abstract (or just overridden) method.

Below is a test with 6 enumeration 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 40,000,000 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 with the parameters w / -server -XX:+PrintCompilation . Of course, the difference is small. However, this is not an interesting problem. If you test the version with 2 enum constants, the result can be significantly different. For 2 call sites, the compiler generates code by setting the appropriate method. In the above test, which will remove the entire booleanValue call and may even execute the test in O (1).

The funniest part, however, comes from 2 to 3 enum constants, when the compiler starts using the built-in caches, and then the WOW constant and magic all change.

<h / "> The bottom line is that the correct test is really complex and includes some knowledge about how to compile JIT, when the GC can be a problem (either remove it or hug it), and so on.
References:

+1
source

A better solution would be something like this:

listing:

 public interface ACServices { public static enum MessageType { // periodic needs to saved in DB PIPE_INFO_TYPE_AC_DEVICE_LIST, // periodic from littlecloud PIPE_INFO_TYPE_DEV_ONLINE, PIPE_INFO_TYPE_DEV_OFFLINE, PIPE_INFO_TYPE_EVENT_LOG, PIPE_INFO_TYPE_DEV_DETAIL, }; 

Implementation:

 ACServices.MessageType msgType = ACServices.MessageType.valueOf(acResponse.getType()); switch (msgType){ case INT_INFO_DEV_STATUS: break; case INT_INFO_DEV_TZ: break; case PIPE_INFO_DEV_COUNT: break; case PIPE_INFO_TYPE_AC_DEVICE_LIST: break; case PIPE_INFO_TYPE_CONFIG_GET_TEXT: break; default: break; } 

Man Pak Hong, Dave ( manpakhong@hotmail.com )

+1
source

Source: https://habr.com/ru/post/893401/


All Articles