Simple Enum
Assuming the user has a simple enum
that does not have any member variable as follows:
public enum Grades { A, B, C, D, E, F }
In this case, we do not need to create a map to get an enum value from an input string. We can use the enum build-in method valueOf
which returns the enum constant of the specified enum type with the specified name.
Please note that the input string must match exactly an identifier used to declare an enum constant in this type. Extraneous white-space characters or wrong letter case are not permitted.
An Enum that has a member variable
Switching to a more interesting example, we assume the user has an enum type that each enum constant has a member variable. In the following example each constant has an int value as member.
public enum Weekday { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7); public int getCode() { return mCode; } Weekday(final int code) { mCode = code; } private final int mCode; }
In this example, getting the matching enum constant for an input integer number is not as straight forward as before. We could iterate over all constants check their code and return the one that matches but that would not be efficient, especially for enum types that have many constants.
What we propose is to create a static map of elements that will have on the key part the value of the enum constant and on the value part the actual enum constant.
public enum Weekday { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7); public int getCode() { return mCode; } public static Weekday fromCode(final int code) { final Weekday element = sMap.get(code); if (element != null) { return element; } throw new IllegalArgumentException(String.format("Unknown code: <%d> for %s", code, Weekday.class.toString())); } Weekday(final int code) { mCode = code; } private static final Map<Integer, Weekday> sMap = new HashMap<>(); static { for (final Weekday d : Weekday.values()) { sMap.put(d.getCode(), d); } } private final int mCode; }
What we did in the above code, was to statically
create the map we described. Which is, a map from the value that each enum constant holds to the constant. Then using the fromCode
method we defined, we check the map for the input value, if the value is not part of the keys set of the map, it will throw an Illegal Argument Exception
.
Doing Even better
An improvement we could do in the above code, would be to extract the part of the code that iterates over the fields and creates the map to reuse it. To do that, we need to use Functional Interface
. Meaning we will create a function that takes as input another function that expects some input (if any) and produces as output an element of the type of the key.
Our function that can be used for all enums that their constants hold an int value is the following:
//ToIntFunction represents a function that produces an int-valued result. This is the int-producing primitive specialization for Function. This is a functional interface whose functional method is applyAsInt(Object). public static <E extends Enum<E>> Map<Integer, E> createMap(final ToIntFunction<E> converter, final Class<E> enumClass) { final Map<Integer, E> map = new HashMap<>(); for (final E s : enumClass.getEnumConstants()) { map.put(converter.applyAsInt(s), s); } return map; }
The code of the enum could change to the following:
public enum Weekday { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7); public int getCode() { return mCode; } public static Weekday fromCode(final int code) { final Weekday element = sMap.get(code); if (element != null) { return element; } throw new IllegalArgumentException(String.format("Unknown code: <%d> for %s", code, Weekday.class.toString())); } Weekday(final int code) { mCode = code; } private static final Map<Integer, Weekday> sMap = Collections.unmodifiableMap(createMap(Weekday::getCode, Weekday.class)); private final int mCode; }
The function that we used as input to the new function is the getCode
we defined in the enum. As you can see, it is a function that produces an int-valued result just as the requirement of the ToIntFunction
parameter asks for.
This post is also available in: Greek
In your particular case, why not just use Enum.ordinal()?
Thank you for the feed back, changed the values of the Enum so that they do not match the ordinal values.
This method can be used for any other data type of values.
It could be the case where the values do not define a range starting with 0 or the values could be of another data type like a String. In those case ordinal value is no use.
Why not use https://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html
?
Hello,
The EnumMap is a specialized Map implementation for use with enum type keys.
In our case the Enum is the value of the Map, not the key.
Your solution proposes the other way around.