我喜欢玩enums。
基本上我需要一个内置函数,它返回我在整数中设置的所有枚举标志。
基本上,当我得到110为Tnteger值(6)的枚举值为a = 1,b = 2且c = 4时我想得到b和c作为返回值。
我制作了这段漂亮的代码,但我觉得Java 8必须内置一些东西。
public static GoalType[] flags(int nval)
{
ArrayList<Boolean> lstBits = new ArrayList<>();
intToBinary(nval, lstBits);
ArrayList<GoalType> goals = new ArrayList<>();
GoalType[] arGoals = GoalType.values();
for (int i = 0; i < lstBits.size(); i++)
{
if (lstBits.get(i))
goals.add(arGoals[i]);
}
return goals.toArray(new GoalType[0]);
}
private static void intToBinary(int i, List<Boolean> lst)
{
int remainder = 0;
if (i <= 1)
{
lst.add(i == 0 ? false : true);
}
else
{
remainder = i % 2;
intToBinary(i >> 1, lst);
lst.add(remainder == 0 ? false : true);
}
}
将GoalType定义为具有此正文的枚举:
Undefined(1 << 0), Break(1 << 1), Place(1 << 2), KillMob(1 << 3), KillLPlayer(1 << 4), Deliver(1 << 5), Vote(1 << 6), Search(1 << 7);
public final int value;
GoalType(int nGoal)
{
value = nGoal;
}
也许我需要找一个关键字?
答案 0 :(得分:2)
如问题评论中所述,undefined
(和EnumSet
)是您想要存储枚举或将其用作键等的方法。但是,它们都没有提供您要实施的操作。因此,对于这项具体任务,您可以自己动手,但这并不意味着您无法使用JDK中的一组非常有用的类来完成这项任务,就像手套一样...... / p>
首先,您不需要使枚举的每个元素都封装EnumMap
值。所以,让我们重新定义你的枚举只是一个枚举:
int
然后,让我们创建一个方法,将标志作为输入并返回一个枚举值数组:
enum GoalType {
UNDEFINED, BREAK, PLACE, KILL_MOB, KILL_PLAYER, DELIVER, VOTE, SEARCH
}
这将从public GoalType[] matchingFlags(byte[] flags) {
GoalType[] values = GoalType.values();
return BitSet.valueOf(flags).stream()
.filter(n -> n < values.length) // bounds check
.mapToObj(n -> values[n])
.toArray(GoalType[]::new);
}
参数创建BitSet
,然后调用BitSet.stream
方法,该方法返回IntStream
个索引,其中bitset包含一个位,例如如果flags为flags
,则流将为1000 1010
(这是从最低位到最高位)。
然后我们过滤这些索引以仅保留enum的有效序数值内的索引。 (这是完全可选的,但非常健康;否则如果标志包含的索引大于枚举的最大序数值,则可能会得到1, 3, 7
。
在检查之后,我们将索引映射到实际的枚举值,方法是从包含所有枚举值的数组中获取它,最后我们创建并返回一个包含所需枚举值的数组。
样本用法:
ArrayIndexOutOfBoundsException
如果需要,您可以收集到byte[] flags = new byte[]{6};
GoalType[] goalTypes = this.matchingFlags(flags);
System.out.println(Arrays.toString(goalTypes)); // [BREAK, PLACE]
而不是数组。这是一个适用于每个枚举的通用版本:
EnumSet
用法:
public static <T extends Enum<T>> EnumSet<T> getEnumValuesForFlags(
Class<T> enumType,
byte[] flags) {
T[] values = enumType.getEnumConstants();
return BitSet.valueOf(flags).stream()
.filter(n -> n < values.length) // bounds check
.mapToObj(n -> values[n])
.collect(Collectors.toCollection(() -> EnumSet.noneOf(enumType)));
}
答案 1 :(得分:1)
好的旧C方式 - 为这些枚举定义位掩码,例如:
public static final int MASK_B = 0x0002;
之后,可以通过以下方式轻松计算位标志:
int bitFlag = nval & MASK_B
[编辑]是的,只需使用EnumSet
作为@VGR建议。
答案 2 :(得分:1)
@ Federico的答案很好,但有时你不能依赖Java支持枚举的整数。有时您需要保证特定的映射。在这些时候,有两种合理的方法,你使用它们取决于具体情况。
首选方式,假设您需要保证映射:
enum MyEnumeration
{
// Normally you may have just done as in the following commented line:
// EnumA, EnumB, EnumC;
// Instead you should...
// Give the enum a constructor in which you supply the mapping
EnumA (1),
EnumB (2),
EnumC (3);
private int value;
MyEnumeration(int n)
{
value = n;
}
// Now you can request the mapping directly from any enum value too
public int getValue()
{
return value;
}
// You can also request the enum value by providing the integer
public static MyEnumeration fromValue(int value)
{
// for improved performance (constant time), replace this lookup with a static HashMap
for(MyEnumeration e : values())
if(e.value == value)
return e;
return null;
}
}
这使您可以保证最终的特定映射,在我的案例中,这在最近的工作中是必不可少的。
有时,特定的映射不是枚举的一个整体(无双关语)部分。有时,某些其他对象需要查看枚举,就好像它具有特定的映射一样。在这些情况下,通常首选不将映射放在枚举本身中,如上所述,因为在这种情况下枚举不应该知道映射。
在这种情况下,您基本上只需创建HashMap
来为您执行查找。它可能很烦人,特别是如果你有很多价值观,但它是值得的。
MyEnumerationConversion
{
private static Map<MyEnumeration, Integer> toInt = new HashMap<MyEnumeration, Integer>();
private static Map<Integer, MyEnumeration> toEnum = new HashMap<Integer, MyEnumeration>();
public static void initialize()
{
// populate the maps here however you want
}
public static int toInt(MyEnumeration e) { return toInt.get(e); }
public static MyEnumeration toEnum(Integer i) { return toEnum.get(i); }
}
实际上,这允许您将任何对象映射到任何其他对象,而不仅仅是enum-to-int / int-to-enum,但它确实很好地处理了enum-int-enum情况。
现在,当您想转换时,只需MyEnumerationConversion.toEnum(2);
或MyEnumerationConversion.toInt(MyEnumeration.EnumB);
注意:虽然泛型参数和函数参数是Integer
,但您可以为它们提供int
,因为Java将为您在int
和Integer
之间进行转换在称为“boxing”(int-&gt; Integer)或“unboxing”(Integer-&gt; int)的转换中。因为Java不支持基本类型作为通用参数,所以在这里使用Integer
是必要的。
在我最近完成的代码中,我使用了上述两个版本 - 作为构造函数参数的值和Map
版本。 Map
(第二个)版本,我用于您遇到的情况,将整数中的位标记映射到一组枚举值,反之亦然。我使用EnumSet
作为int-to-enum转换的返回值,我还使用EnumSet
作为执行enum-to-int转换的函数的参数。
以下两个代码段假设为First Method
。如果使用Second Method
,请使用适当的替代方法替换MyEnumeration.toInt
/ MyEnumeration.fromInt
。
现在您需要来自EnumSet
的位图?
// You could use a stream instead. I just happen to think they are less readable
public int toBitMap(EnumSet set)
{
int map = 0;
for(MyEnumeration e : set)
map |= e.getValue();
return map;
}
或来自位图的EnumSet
?
public EnumSet<MyEnumeration> fromBitMap(int map)
{
int highestOneBit = Integer.highestOneBit(map);
EnumSet<MyEnumeration> enumSet = EnumSet.noneOf(MyEnumeration.class);
// this loop will iterate over powers of two up to map's highest set bit
// that is: 1, 2, 4, 8, etc., but will not iterate farther than needed
for(int i = 1; i <= highestOneBit; i <<= 1)
if( (map&i) != 0 )
enumSet.add( MyEnumeration.fromValue(i) );
return enumSet;
}
上面我在代码示例// for improved performance, replace this lookup with a static HashMap
中删除了简单注释。当我写这篇文章时,我完全没有想到Enum
的静态性质。
不幸的是,你不能(在写这篇文章的时候)做这样的事情:
// BAD code
enum MyEnum
{
A (5), B (10);
int v;
static Map<Integer, MyEnum> map = new HashMap<Integer, MyEnum>();
MyEnum(int _v) { v = _v; map.put(v, this); }
MyEnum fromInt(int v) { return map.get(v); }
}
这将导致在初始化程序中使用静态字段时出现编译错误。
相反,您需要在地图上执行延迟初始化。您应该可以通过将map
初始化为null,检查if(map != null)
中的fromInt
并返回map.get
结果来执行此操作,否则(如果map为null,它将会在第一次调用fromInt
时,创建地图然后返回get
结果。
// Better version
enum MyEnum
{
A (5), B (10);
private int value;
private static Map<Integer, MyEnum> map = null;
MyEnum(int v)
{
value = v;
}
public int getInt()
{
return value;
}
public static MyEnum fromInt(int v)
{
if(map != null)
{
return map.get(v);
}
else
{
initializeMap();
return map.get(v);
}
}
private void initializeMap()
{
map = new HashMap<Integer, MyEnum>();
for(MyEnum e : values())
map.put(e.getInt(), e);
}
}