3

I have an enum as follows:

public enum ServerTask {

HOOK_BEFORE_ALL_TASKS("Execute"),
COPY_MASTER_AND_SNAPSHOT_TO_HISTORY("Copy master db"),
PROCESS_CHECKIN_QUEUE("Process Check-In Queue"),
...
}

I also have a string (lets say string = "Execute") which I would like to make into an instance of the ServerTask enum based on which string in the enum that it matches with. Is there a better way to do this than doing equality checks between the string I want to match and every item in the enum? seems like this would be a lot of if statements since my enum is fairly large

GregH
  • 5,125
  • 8
  • 55
  • 109
  • 4
    You only need a single if while looping over `ServerTask.values()` :) – NiziL May 26 '15 at 16:46
  • Are you checking against the name of the enum (e.g., `HOOK_BEFORE_ALL_TASKS`) or the value of that parameter (`"Execute"`)? If it's the parameter it'd help if you put the code of the getter for it or the name if it's not `private`. – Captain Man May 26 '15 at 17:13

4 Answers4

5

At some level you're going to have to iterate over the entire set of enumerations that you have, and you'll have to compare them to equal - either via a mapping structure (initial population) or through a rudimentary loop.

It's fairly easy to accomplish with a rudimentary loop, so I don't see any reason why you wouldn't want to go this route. The code snippet below assumes the field is named friendlyTask.

public static ServerTask forTaskName(String friendlyTask) {
    for (ServerTask serverTask : ServerTask.values()) {
        if(serverTask.friendlyTask.equals(friendlyTask)) {
            return serverTask;
        }
    }
    return null;
}

The caveat to this approach is that the data won't be stored internally, and depending on how many enums you actually have and how many times you want to invoke this method, it would perform slightly worse than initializing with a map.

However, this approach is the most straightforward. If you find yourself in a position where you have several hundred enums (even more than 20 is a smell to me), consider what it is those enumerations represent and what one should do to break it out a bit more.

Makoto
  • 104,088
  • 27
  • 192
  • 230
1

Create static reverse lookup map.

public enum ServerTask {

  HOOK_BEFORE_ALL_TASKS("Execute"),
  COPY_MASTER_AND_SNAPSHOT_TO_HISTORY("Copy master db"),
  PROCESS_CHECKIN_QUEUE("Process Check-In Queue"),
  ...
  FINAL_ITEM("Final item");

  // For static data always prefer to use Guava's Immutable library
  // http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableMap.html

  static ImmutableMap< String, ServerTask > REVERSE_MAP;

  static
  {
    ImmutableMap.Builder< String, ServerTask > reverseMapBuilder =
      ImmutableMap.builder( );

    // Build the reverse map by iterating all the values of your enum
    for ( ServerTask cur : values() )
    {
       reverseMapBuilder.put( cur.taskName, cur );
    }

    REVERSE_MAP = reverseMapBuilder.build( );
  }

  // Now is the lookup method
  public static ServerTask fromTaskName( String friendlyName ) 
  {
    // Will return ENUM if friendlyName matches what you stored
    // with enum
    return REVERSE_MAP.get( friendlyName );
  }

}
Alexander Pogrebnyak
  • 44,836
  • 10
  • 105
  • 121
  • `Maps.uniqueIndex()` would make this considerably less verbose. – Matt Ball May 26 '15 at 16:47
  • 3
    Any way you can add a few comments to illustrate what is going on – GregH May 26 '15 at 16:47
  • @jeremy. What is not clear? If you ask I will answer. – Alexander Pogrebnyak May 26 '15 at 16:48
  • 3
    Not the DV, but a reason that comes to mind is that you just code dumped and you haven't explained how any of this code works. By Jeremy asking for comments to illustrate what's happening, I think it's clear that he's not just looking for a solution but he actually wants to understand why you're doing what you are, and how it works. – Aify May 26 '15 at 16:59
1

If you have to get the enum from the String often, then creating a reverse map like Alexander suggests might be worth it.

If you only have to do it once or twice, looping over the values with a single if statement might be your best bet (like Nizil's comment insinuates)

for (ServerTask task : ServerTask.values())
{
    //Check here if strings match
}

However there is a way to not iterate over the values at all. If you can ensure that the name of the enum instance and its String value are identical, then you can use:

ServerTask.valueOf("EXECUTE")

which will give you ServerTask.EXECUTE.

Refer this answer for more info.

Having said that, I would not recommend this approach unless you're OK with having instances have the same String representations as their identifiers and yours is a performance critical application which is most often not the case.

Srini
  • 1,626
  • 2
  • 15
  • 25
  • ServerTask.valueOf(...) is what you are looking for. If the enums are all upper case, then you can also use ServerTask.valueOf(input.toUpperCase()); – Jamie May 26 '15 at 17:33
  • @Jamie, in his example, the `enum` instance identifiers and their `String` values don't match (not just case wise). For e.g., `HOOK_BEFORE_ALL_TASKS` has a `String` value of `"EXECUTE"` – Srini May 26 '15 at 17:38
0

You could write a method like this:

static ServerTask getServerTask(String name)
{
    switch(name)
    {
         case "Execute": return HOOK_BEFORE_ALL_TASKS;
         case "Copy master db": return COPY_MASTER_AND_SNAPSHOT_TO_HISTORY;
         case "Process Check-In Queue": return PROCESS_CHECKIN_QUEUE;
    }
}

It's smaller, but not automatic like @Alexander_Pogrebnyak's solution. If the enum changes, you would have to update the switch.

Jamie
  • 1,888
  • 1
  • 18
  • 21