因此,在C++/C#中,您可以创建标记枚举以保存多个值,并且在数据库中存储单个有意义的整数当然是微不足道的.
在Java中你有EnumSets,它似乎是一种在内存中传递枚举的好方法,但是你如何将组合的EnumSet输出到一个整数来存储呢?还有另一种方法来解决这个问题吗?
Tho*_*ung 16
将序数存储为EnumSet的表示并不是一个好主意.序数取决于Enum类中定义的顺序(相关讨论在这里).您的数据库可能很容易通过重构来破坏,该重构会更改枚举值的顺序或在中间引入新的值.
您必须引入单个枚举值的稳定表示.这些可以再次为int值,并以EnumSet的建议方式表示.
您的枚举可以实现接口,因此稳定表示可以直接在枚举值中(改编自Adamski):
interface Stable{
int getStableId();
}
public enum X implements Stable {
A(1), B(2);
private int stableId;
X(int id){
this.stableId = id;
}
@Override public int getStableId() {
return stableId;
}
}
Run Code Online (Sandbox Code Playgroud)
改编自Adamski的代码:
public <E extends Stable> int encode(EnumSet<E> set) {
int ret = 0;
for (E val : set) {
ret |= (1 << val.getStableId());
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
Ada*_*ski 12
提供你的枚举适合int(即有<= 32个值)我会通过使用每个枚举的序数值来推动我自己的实现; 例如
public <E extends Enum<E>> int encode(EnumSet<E> set) {
int ret = 0;
for (E val : set) {
// Bitwise-OR each ordinal value together to encode as single int.
ret |= (1 << val.ordinal());
}
return ret;
}
public <E extends Enum<E>> EnumSet<E> decode(int encoded, Class<E> enumKlazz) {
// First populate a look-up map of ordinal to Enum value.
// This is fairly disgusting: Anyone know of a better approach?
Map<Integer, E> ordinalMap = new HashMap<Integer, E>();
for (E val : EnumSet.allOf(enumKlazz)) {
ordinalMap.put(val.ordinal(), val);
}
EnumSet<E> ret= EnumSet.noneOf(enumKlazz);
int ordinal = 0;
// Now loop over encoded value by analysing each bit independently.
// If the bit is set, determine which ordinal that corresponds to
// (by also maintaining an ordinal counter) and use this to retrieve
// the correct value from the look-up map.
for (int i=1; i!=0; i <<= 1) {
if ((i & encoded) != 0) {
ret.add(ordinalMap.get(ordinal));
}
++ordinal;
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
免责声明:我没有测试过这个!
编辑
正如托马斯在评论中提到的那样,序数是不稳定的,因为代码中对枚举定义的任何更改都会导致数据库中的编码损坏(例如,如果在现有定义的中间插入新的枚举值).我解决这个问题的方法是为每个枚举定义一个"枚举"表,其中包含一个数字ID(不是序数)和String枚举值.当我的Java应用程序启动时,DAO层所做的第一件事就是将每个Enum表读入内存,并且:
这比我上面描述的序数方法更清晰/更强大的恕我直言.
// From Adamski's answer
public static <E extends Enum<E>> int encode(EnumSet<E> set) {
int ret = 0;
for (E val : set) {
ret |= 1 << val.ordinal();
}
return ret;
}
@SuppressWarnings("unchecked")
private static <E extends Enum<E>> EnumSet<E> decode(int code,
Class<E> enumType) {
try {
E[] values = (E[]) enumType.getMethod("values").invoke(null);
EnumSet<E> result = EnumSet.noneOf(enumType);
while (code != 0) {
int ordinal = Integer.numberOfTrailingZeros(code);
code ^= Integer.lowestOneBit(code);
result.add(values[ordinal]);
}
return result;
} catch (IllegalAccessException ex) {
// Shouldn't happen
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
// Probably a NullPointerException, caused by calling this method
// from within E's initializer.
throw (RuntimeException) ex.getCause();
} catch (NoSuchMethodException ex) {
// Shouldn't happen
throw new RuntimeException(ex);
}
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,没有人建议使用维护良好的库,而不是编写自己的库。上面的答案是正确的和有教育意义的,但它只是鼓励人们复制和粘贴代码(然后大多忘记学分)。
这是我的 2 美分:
EnumSet<YourEnum> mySet = EnumSet.of(YourEnum.FIRST);
long vector = EnumUtils.generateBitVector(YourEnum.class, mySet);
EnumSet<YourEnum> sameSet = EnumUtils.processBitVector(YourEnum.class, vector);
Run Code Online (Sandbox Code Playgroud)
见https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/EnumUtils.html
如果您查看源代码RegularEnumSet
,这是Enum的<= 64个成员的实现,您将看到它包含:
/**
* Bit vector representation of this set. The 2^k bit indicates the
* presence of universe[k] in this set.
*/
private long elements = 0L;
Run Code Online (Sandbox Code Playgroud)
元素是位掩码,位位置等于枚举,这正是您所需要的.但是,这个属性不能通过getter或setter使用,因为它不匹配的等效访问器JumboEnumSet
.
它不是最好的解决方案之一,但如果简单性和速度是您所追求的,您可以创建2个静态实用程序方法,elements
使用反射检索和设置属性.
对我来说,我可能只是设置一个常量类,将枚举值保存为整数常量,我可以确定哪个枚举被分配了什么位.