有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在switchcase语句中使用枚举的序数值

对于我的项目,我使用的是枚举,我需要实现switch case语句,其中检查特定枚举值的序号,如下所示:

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

注意:返回值只是一个示例

不幸的是,Eclipse(我使用的是1.6 JDK)给出了我的编译错误“案例” 表达式必须是常量表达式”。我应该做什么?除了静态查找表,还有其他方法吗,这里描述:Convert from enum ordinal to enum type


共 (6) 个答案

  1. # 1 楼答案

    答案的目标是@RIA对常量vs方法枚举和性能原因的评论,它没有直接回答OP问题,所以我认为它可以被视为噪声。 然而,我相信了解内部工作原理是很重要的

    我将基准测试从他的示例中去掉,并对其进行了改进,以消除占执行时间90%以上的垃圾收集和字符串创建。增加了预热阶段,以确保hotspot实际编译方法

    还有一些,基准测试实际上是调用站点测试。调用站点的优化对于1、2和更多都有很大不同。调用站点是对抽象(或刚刚重写)方法的调用

    以下是6个枚举常量的测试:

    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 
    

    代码是用/-server -XX:+PrintCompilation选项运行的。 当然,差别并不大。然而,这不是一个有趣的问题。但是,如果使用2个枚举常量测试该版本,结果可能会显著不同。对于2个调用站点,编译器通过内联相关方法生成代码。在上面的测试中,这将删除对booleanValue的整个调用,甚至可以让您在O(1)中执行测试

    然而,最有趣的是,当编译器开始使用内联缓存时,从2个枚举常量变为3个枚举常量,然后是常量,然后一切都变了


    底线是:正确的基准测试确实很难,需要了解JIT是如何编译的,何时GC可能会出现问题(要么删除它,要么接受它),等等。
    链接
  2. # 2 楼答案

    如果你在某处有一个序列化的序数,就是这样做的。不过,保持枚举的通常方法是按其名称,而不是按顺序。此外,除非尝试实现EnumMap/Set之类的功能,否则在正常情况下不应使用ordinal。当然,枚举可以只是C类的一个端口,处理不可避免的int,需要对枚举对象进行转换

    只需使用Enum.values()获得一个按ordinal()排序的数组,因为数组每次都被克隆,所以保持一个ref是可以的

    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:
    ...
       }
    }
    

    注意到你只需要对和错,这是一种固定的行为。你可以使用java。util。枚举集或简单的long,如果感觉勇敢(且枚举常量不超过64个)。例如:

    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));
    }
    
  3. # 3 楼答案

    只需使用枚举常量:

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

    假设:

    public enum MyEnum {
        A, B
    }
    

    但是要小心NullPointerException(如果variablenull

  4. # 4 楼答案

    首先,你不应该太依赖序数。如果可能的话,将变量设置为String(并使用Enum.valueOf(string)转换为enum),或者最多设置为enum

    如果你真的不能,那就用enum.values()[ordinal]。然后使用交换机中的枚举

  5. # 5 楼答案

    你想要的可能是: 如果在枚举本身的方法中需要开关:

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

    在另一个班级:

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

    但是,如果添加另一个enum,很容易忘记更新switch语句,因此更好的选择是在enum本身中放置类似的方法,并使用特定于常量的方法实现:

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

    这样,如果您添加了一个新的枚举值,编译器会提醒您声明getBooleanValue方法,您只需在需要的地方使用A.getBooleanValue();

    正如评论中指出的,另一种选择是:

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

    这是一个偏好问题,具体情况会有所不同。如果只是为每个枚举返回一个值,那么构造函数的版本是合理的,但我发现它的可读性较差。通过测试可以看出,对这种性能更好的担忧是毫无根据的:

    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);
        }
    
  6. # 6 楼答案

    更好的解决方案是这样的:

    枚举:

    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,
        };
    

    实施:

    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;
    }
    

    戴夫,文伯康(manpakhong@hotmail.com)