Can I get an enum based on the value of its field?

Solution 1:

For reference, here is an alternative solution with a HashMap:

enum CrimeCategory {
  ASBO("Anti Social Behaviour"),
  BURG("Burglary"),
  CRIMDAM("Criminal Damage And Arson"),
  DRUGS("Drugs"),
  OTHTHEFT("Other Theft"),
  PUPDISOR("Public Disorder And Weapons"),
  ROBBERY("Robbery"),
  SHOPLIF("Shoplifting"),
  VEHICLE("Vehicle Crime"),
  VIOLENT("Violent Crime"),
  OTHER("Other Crime");

  private static final Map<String, CrimeCategory> map = new HashMap<>(values().length, 1);

  static {
    for (CrimeCategory c : values()) map.put(c.category, c);
  }

  private final String category;

  private CrimeCategory(String category) {
    this.category = category;
  }

  public static CrimeCategory of(String name) {
    CrimeCategory result = map.get(name);
    if (result == null) {
      throw new IllegalArgumentException("Invalid category name: " + name);
    }
    return result;
  }
}

Solution 2:

Add a static method to the CrimeCategory enum:

public static CrimeCategory valueOf(String name) {
    for (CrimeCategory category : values()) {
        if (category.category.equals(name)) {
            return category;
        }
    }    
    throw new IllegalArgumentException(name);
}

Solution 3:

Static factory methods that return an enum constant based on the value of an instance field take on one of the two forms described in the other answers: a solution based on iterating the enum values, or a solution based on a HashMap.

For enums with a small number of constants, the iterative solution should be as performant as the HashMap solution (which requires calculation of the hash code, matching it to a bucket, and assuming that there will be no hash collisions).

For larger enums, the map-based solution will be more performant (but requires storage space in memory). However, if the factory method is invoked infrequently then the overall performance improvement by using a map could still be immeasurably small.

The overall decision to use an iterative lookup or a map-based lookup for the static factory method will ultimately depend on your requirements and the environment. It is never wrong to start with an iterative lookup and then change to a map-based implementation if profiling shows an actual performance problem.

Lastly, since Java 8, the Streams API enables a pipeline-based solution for mapping (that should have performance similar to the iterative solution). For example, say that you want to create an interface that you could use on any enum class to express your intent that it should be matchable by one of its instance fields. Let's call this interface Matchable. This interface defines a method which returns the instance field on which you want to match (eg. getField()). This interface can also define a static factory method to return a constant from any implementing enum class:

interface Matchable {

    Object getField();

    public static <E extends Enum<E> & Matchable> E forToken(Class<E> cls, Object token) {

        return Stream.of(cls.getEnumConstants())
            .filter(e -> e.getField().equals(token))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("Unknown token '" +
                    token + "' for enum " + cls.getName()));
    }
}

Now, any enum class that you define that implements Matchable can use the Matchable.forToken() static factory method to find the enum constant whose instance field value matches the supplied parameter.

The generic type declaration E extends Enum<E> & Matchable assures that the type token passed to the method as a parameter will be for an enum class that implements Matchable (otherwise the code won't compile).