@Federico's answer is good, but sometimes you just cannot rely on Java's integers backing the enums. Sometimes you need to guarantee a specific mapping. For these times, there are two reasonable approaches, and which you use depends on the situation.
First Method
The preferred way, assuming you need to guarantee the mapping:
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;
}
}
This allows you to guarantee the specific mappings that you end up with, which in my case has been necessary a lot in recent work.
Sometimes the specific mapping is not an integral (no pun) part of the enumeration. Sometimes some other object needs to view the enumeration as if it had a specific mapping. In those cases, it is generally preferred to not put the mapping in the enumeration itself, as was done above, since the enumeration should not know about the mapping in this case.
Second Method
In this case, you basically just create HashMap
s to do the lookup for you. It can be annoying, especially if you have a lot of values, but it can be worth it.
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); }
}
In fact, this allows you to map any object to any other object, not just enum-to-int/int-to-enum, but it does handle the enum-int-enum situation well.
Now when you want to convert, you just MyEnumerationConversion.toEnum(2);
or MyEnumerationConversion.toInt(MyEnumeration.EnumB);
Note: Although the generic parameter and the function arguments are Integer
, you can supply an int
to them, as Java will convert between int
and Integer
for you in a conversion called "boxing" (int->Integer) or "unboxing" (Integer->int). Using Integer
was necessary here since Java does not support primitive types as generic parameters.
In code I have done recently, I have used both of the above two versions - the value as constructor argument, and the Map
version. The Map
(second) version, I used for exactly the situation you are encountering, mapping bit flags in integers to a set of enumeration values, and the other way around. And I used EnumSet
as the return value for the int-to-enum conversion and I used EnumSet
also as the parameter to the function that did the enum-to-int conversion.
Bit Maps
Below two code snippets assume First Method
. If using Second Method
, then replace MyEnumeration.toInt
/MyEnumeration.fromInt
with the appropriate alternative methods.
So now you need a bit map from your 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;
}
Or an EnumSet
from your bit map?
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;
}
Optimization note concerning First Method
Above I dropped the simple comment in a code example // for improved performance, replace this lookup with a static HashMap
. When I wrote that, I was completely not thinking at the moment about the static nature of an Enum
.
Unfortunately, you cannot (at time of this writing) do something like this:
// 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); }
}
That will result in a compile error about using the static field in the initializer.
Instead, you will need to perform a delayed-initialization on the map. You should be able to do that by initializing map
to null, checking if(map != null)
in fromInt
, and returning the map.get
result, otherwise (if map is null, which it will be the first time fromInt
is called), create the map then return the get
result.
// 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);
}
}