对于我的项目,我正在使用枚举,并且我需要实现switch-case语句,在其中检查特定Enum的值的序数,如下所示:
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吗?
这是完成的,只要您在某个地方有某种顺序的序数。保持枚举的通常方法是按其名称而不是按序排列。同样,在正常情况下,您不应该使用序数,除非尝试实现类似EnumMap / Set的东西。当然,枚举可以只是来自C的东西的端口,并且处理不可避免的int,需要转换为Enum对象。
只需使用Enum.values()
来获得由ordinal()
排序的数组,因为每次都会克隆该数组,因此保持对它的引用是可以的。
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:
...
}
}
刚刚注意到您只需要是非就可以,这是Set的行为。如果您很勇敢(并且枚举常量不超过64个),则可以使用java.util.EnumSet或简单的long
。例如:
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));
}
首先,您不应该过多地依赖序数。如果可能,将变量设置为String
(并使用enum
转换为Enum.valueOf(string)
,或者最好将其设置为enum
。
如果确实不能,请使用enum.values()[ordinal]
。然后使用开关中的枚举。
答案的目标是@Riaan对常量vs方法枚举和性能原因的评论,它不能直接回答OP问题,因此我认为这可以视为噪音。但是,我认为了解内部工作原理很重要。
我从他的示例中删除了基准,并对其进行了改进,以消除占用90%以上执行时间的垃圾收集和字符串创建。添加了预热阶段,以确保热点实际编译方法。
还有更多,该基准实际上是呼叫站点测试。呼叫站点的优化对于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热身...148 1%t1.ZEnums :: testEnum @ 7(125字节)150 1 t1.ZEnums $ MyEnum $ 6 :: getBooleanValue(2个字节)152 2 t1.ZEnums $ MyEnum $ 1 :: getBooleanValue(2个字节)154 3 t1.ZEnums $ MyEnum $ 2 :: getBooleanValue(2个字节)155 4 t1.ZEnums $ MyEnum $ 3 :: getBooleanValue(2个字节)158 2%t1.ZEnums :: testAlt @ 7(125字节)162 5 t1.ZEnums :: testEnum(125字节)164 6 t1.ZEnums :: testAlt(125个字节)21:08:46.716热身:160000021:08:46.716测试40000000次迭代21:08:46.716 ====21:08:46.716使用覆盖方法进行测试...21:08:47.513 替代方法版本花费781ms,长度:16000000021:08:47.513使用c-tor中的常数进行测试...21:08:48.138 c-tor版本的常数用了625ms,长度:160000000
该代码使用-server -XX:+PrintCompilation
选项运行。当然,差异并不大。但这不是有趣的问题。但是,如果使用2个枚举常量测试版本,则结果可能会大不相同。对于2个呼叫站点,编译器通过插入相关方法来生成代码。在上面的测试中,这将删除对booleanValue的整个调用,甚至可以在O(1)中执行测试。
然而,最有趣的部分是,当编译器开始使用内联高速缓存,然后使用该常量时,枚举常量将从2变为3,而WOW神奇的是,一切都会改变。
只需使用枚举常量:
MyEnum variable;
...
switch ( variable ) {
case A:
return true;
case B:
return true;
default:
return false;
}
假设类似:
public enum MyEnum {
A, B
}
但是要小心NullPointerException
(如果variable
为null
)
您要的可能是:如果您需要在枚举本身的方法中进行切换:
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;
}
但是,如果添加另一个枚举,则很容易忘记更新switch语句,因此更好的选择是将这样的方法放入枚举本身,并使用特定于常量的方法实现:
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);
}
更好的解决方案是这样的:
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;
}
Man Pak Hong,Dave([email protected])
这是因为编译器看到了差异。例如下面的枚举代码,我们可以看到:
public enum TrafficLight {RED, YELLOW, GREEN}
TrafficLight trafficLights = ...
switch (trafficLights) {
case RED: {/* do stuff */}
case YELLOW: {/* do stuff */}
case GREEN: {/* do stuff */}
}
但编译器请参阅:
switch (trafficLights.ordinal()) {
case 0: {/* do stuff */}
case 1: {/* do stuff */}
case 2: {/* do stuff */}
}
这就是为什么当trafficlights为NULL时它会抛出NPE的原因,即使我们没有调用该方法,您也不知道为什么在ordinal()函数中它会抛出NPE的原因。
SOLUTION在到达交换条件之前将ENUM检查为NULL。
trafficLights != null