65

Is there anyway to check if an enum exists by comparing it to a given string? I can't seem to find any such function. I could just try to use the valueOf method and catch an exception but I'v been taught that catching runtime exceptions is not good practice. Anybody have any ideas?

bluish
  • 23,093
  • 23
  • 110
  • 171
Danny
  • 4,454
  • 6
  • 36
  • 55
  • 9
    I really don't understand the idea behind emum valueOf throwing an exception... it does not make any sense. It would be a lot more practical in every aspect if it would just return NULL. – marcolopes Feb 06 '13 at 08:11
  • @marcolopes: One reason would be wanting to cover every possible cases with Enum. If an Enum isn't found, it means the dev should be notified as soon as possible that there's a missing case. It shouldn't let the program throw a NullPointerError somewhere else, later in the code. – Eric Duminil Feb 07 '19 at 11:23
  • @EricDuminil a null result would be simpler... that's what i do! I basically don't use valueOf on my code, and write a new method inside the enum get(value) that catches the exception... – marcolopes Feb 10 '19 at 21:20

9 Answers9

71

If I need to do this, I sometimes build a Set<String> of the names, or even my own Map<String,MyEnum> - then you can just check that.

A couple of points worth noting:

  • Populate any such static collection in a static initializer. Don't use a variable initializer and then rely on it having been executed when the enum constructor runs - it won't have been! (The enum constructors are the first things to be executed, before the static initializer.)
  • Try to avoid using values() frequently - it has to create and populate a new array each time. To iterate over all elements, use EnumSet.allOf which is much more efficient for enums without a large number of elements.

Sample code:

import java.util.*;

enum SampleEnum {
    Foo,
    Bar;

    private static final Map<String, SampleEnum> nameToValueMap =
        new HashMap<String, SampleEnum>();
    
    static {
        for (SampleEnum value : EnumSet.allOf(SampleEnum.class)) {
            nameToValueMap.put(value.name(), value);
        }
    }
    
    public static SampleEnum forName(String name) {
        return nameToValueMap.get(name);
    }
}

public class Test {
    public static void main(String [] args)
        throws Exception { // Just for simplicity!
        System.out.println(SampleEnum.forName("Foo"));
        System.out.println(SampleEnum.forName("Bar"));
        System.out.println(SampleEnum.forName("Baz"));
    }
}

Of course, if you only have a few names this is probably overkill - an O(n) solution often wins over an O(1) solution when n is small enough. Here's another approach:

import java.util.*;

enum SampleEnum {
    Foo,
    Bar;

    // We know we'll never mutate this, so we can keep
    // a local copy.
    private static final SampleEnum[] copyOfValues = values();
    
    public static SampleEnum forName(String name) {
        for (SampleEnum value : copyOfValues) {
            if (value.name().equals(name)) {
                return value;
            }
        }
        return null;
    }
}

public class Test {
    public static void main(String [] args)
        throws Exception { // Just for simplicity!
        System.out.println(SampleEnum.forName("Foo"));
        System.out.println(SampleEnum.forName("Bar"));
        System.out.println(SampleEnum.forName("Baz"));
    }
}
Riccardo
  • 426
  • 5
  • 14
Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • `values()` creates and populates a new array? I don't remember hearing that before, but presumably you have a source? – Michael Myers Jul 22 '09 at 20:27
  • 11
    Well it returns an array... and it can't protect you from mutating that array... and it wouldn't subsequent callers to be hampered by that. Other than that, look at the JRE sources :) – Jon Skeet Jul 22 '09 at 20:29
  • It's not in the JRE sources, that's why I asked. But I hadn't thought of the mutation aspect; you're probably right, then. – Michael Myers Jul 22 '09 at 20:31
  • 2
    Oops, sorry - yes, just checked myself. Decompile an enum then :) It uses clone() which may be pretty quick, but not as quick as not having to do it at all... – Jon Skeet Jul 22 '09 at 20:32
  • Ah, the question of how `values()` works was asked and answered within the last day: http://stackoverflow.com/questions/1163076/how-is-values-implemented-for-java-6-enums/1163121#1163121. – Michael Myers Jul 22 '09 at 20:33
  • I bet the cost of clone is minimal to checking through the array (particularly if the target `String` is not interned.) – Tom Hawtin - tackline Jul 22 '09 at 21:02
  • 1
    Are there any guidelines as to what "size" enums benefit from the Map-based approach vs. the array/loop approach? (I'm not looking for hard/fast rules, just a general ballpark of when to think about doing one way vs. the other) – cdeszaq May 12 '14 at 19:28
  • 1
    @cdeszaq: I suspect it could vary significantly between JVMs and situations. If it's something you're concerned may be significant in your app, I suggest you do performance tests. Sorry not to be able to be more helpful :( – Jon Skeet May 12 '14 at 19:37
55

I don't think there's a built-in way to do it without catching exceptions. You could instead use something like this:

public static MyEnum asMyEnum(String str) {
    for (MyEnum me : MyEnum.values()) {
        if (me.name().equalsIgnoreCase(str))
            return me;
    }
    return null;
}

Edit: As Jon Skeet notes, values() works by cloning a private backing array every time it is called. If performance is critical, you may want to call values() only once, cache the array, and iterate through that.

Also, if your enum has a huge number of values, Jon Skeet's map alternative is likely to perform better than any array iteration.

Michael Myers
  • 178,094
  • 41
  • 278
  • 290
45

One of my favorite lib: Apache Commons.

The EnumUtils can do that easily.

Following an example to validate an Enum with that library:

public enum MyEnum {
    DIV("div"), DEPT("dept"), CLASS("class");

    private final String val;

    MyEnum(String val) {
    this.val = val;
    }

    public String getVal() {
    return val;
    }
}


MyEnum strTypeEnum = null;

// test if String str is compatible with the enum 
// e.g. if you pass str = "div", it will return false. If you pass "DIV", it will return true.
if( EnumUtils.isValidEnum(MyEnum.class, str) ){
    strTypeEnum = MyEnum.valueOf(str);
}
Neeraj Singh
  • 406
  • 5
  • 13
рüффп
  • 4,475
  • 34
  • 62
  • 99
  • Thanks for your comment. In fact it is minimal because using libs others did, is often the best solution than re-coding it yourself. If you do it yourself you are arriving to the exact same solution ("Reinventing the wheel") or you miss something important and it behave worst (then in that case you "Reinventing the square wheel") – рüффп Jan 07 '17 at 20:03
  • 1
    Yes exactly. Apache Commons is full kitchen; worth only when you get full or maximum benefit from it :) Square Wheel :P – mumair Jan 07 '17 at 20:10
  • 4
    In my opinion, Apache Commons must be a "common basis dependencies" to add in any Java projects ;) – рüффп Jan 07 '17 at 20:12
  • unfortunately i am using commons-lang-2.6.jar i didnt find EnumUtils.isValidEnum what is the next possible thing ? – Dasari Vinodh Aug 31 '18 at 08:45
  • 1
    @DasariVinodh perhaps it's time to migrate your dependencies? – рüффп Sep 18 '18 at 16:25
  • 1
    This implementation does try catch of valueOf() internally which is exactly what the OP is trying to avoid. I don't know how this is such an upvoted answer. – harogaston Aug 16 '19 at 19:34
  • @harogaston: there is a difference between catching exceptions yourself or delegate it to a library. The OP did not mention to forbid the exception handling at all. The library is supposed to be optimised and under control. At least this make your code cleaner and not managing the exception yourself (and prone to potential errors, that's the way I understood the question). The implementation of the lib can use any techniques and even it can change in the time, your code will not be different. – рüффп Sep 10 '19 at 21:21
  • 1
    @рüффп OP clearly says "but I've been taught that catching runtime exceptions is not good practice" That implementation does exactly that. Plus there is no way to "optimize" the catching of an exception unless you tweak the JVM itself. So this remains not the correct answer. – harogaston Sep 11 '19 at 16:45
  • 1
    Catching runtime exception is not always a bad practice, this should be done with care on specific exceptions. Most of the most used frameworks (e.g. Camel, Spring, and so on) does that and programmers continue to use them without thinking if it's a good practice or not. Catching the Throwable or the root Exception is definitively not a good practice but I have some cases where the 3rd party throws that kind of exception and I have no choice than catching the class Exception in my code. Look at [this answer](https://stackoverflow.com/a/32545382/628006). – рüффп Dec 11 '19 at 23:26
6

I don't know why anyone told you that catching runtime exceptions was bad.

Use valueOf and catching IllegalArgumentException is fine for converting/checking a string to an enum.

bluish
  • 23,093
  • 23
  • 110
  • 171
nos
  • 207,058
  • 53
  • 381
  • 474
  • 26
    No it's not, IMO. That's testing a non-exceptional situation via exceptions - using them for flow control in a normal, non-error condition. That's a very poor use of exceptions IMO, and one which can have a significant performance impact. Exceptions are fine in terms of performance normally, because they shouldn't happen - but when you use them for non-error conditions, then code which *looks* like it should run quickly can get bogged down. – Jon Skeet Jul 22 '09 at 20:56
  • 12
    Funny thing - based on this opinion (which I will normally share as well), I decided to use Apache Commons `EnumUtils.isValidEnum` way to have it as "clean" possible. And guess how `EnumUtils.isValidEnum` is implemented - catching `IllegalArgumentException` of course :-) – Michal Aron Nov 25 '15 at 14:03
  • 1
    Well, if the string you're testing for is supplied by the user, than it is an expected situation to get an IllegalArgumentException, right? Honestly I'm a bit surprised there isn't a method in Enum for this use case. – Johanneke Jul 06 '17 at 08:54
  • @JonSkeet I think you are wrong. Here it s not about flow control, lets take an API, often we will maybe wait string value from an api ( often define by the application and theorically have 100% chance to exist, lets talk about an order "shipped" "pending" "cancelled" ), so if my api accept only 3 values as enum for exemple, and we provide a wrong one, it should raise an exception, which will probably raise a bad request response. – amdev Aug 29 '19 at 13:18
  • 1
    @amdev: It's *very easy* for you to detect that and throw an exception in your own code, if that's what you want. It's far uglier for *other* use cases to have to swallow an exception when the value isn't present. Do you believe `map.get` should throw an exception if the key isn't present, too? It would potentially be cleaner to have two separate methods - one throwing and one not - but while there's only a single method, I think it 's much cleaner for it *not* to throw. – Jon Skeet Aug 29 '19 at 13:30
  • @amdev leaving style considerations aside, the "problem" with exceptions is that the JVM has to prepare a whole stack trace for you, and it takes some time. If you expect a lot of bad requests, maybe it is worth implementing some O(1) verification method in order to avoid the overhead caused by exceptions. – DGoiko Feb 19 '20 at 23:49
  • 1
    Of course an exception is exceptionnal and in my scenario the app send the `order status` and theorically the enum will always match the string. Everything depends of the use case, if you never except that the enum will not match the string so it's exceptionnal and it should raise an exception. If it's a common case yes you should probably just test that your string is correct before to try to convert it, anyway in this scenario, if you test your string before to call valueOf, it still very exceptional that the enum will not match. – amdev Mar 05 '20 at 09:29
6

Based on Jon Skeet answer i've made a class that permits to do it easily at work:

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * <p>
 * This permits to easily implement a failsafe implementation of the enums's valueOf
 * Better use it inside the enum so that only one of this object instance exist for each enum...
 * (a cache could solve this if needed)
 * </p>
 *
 * <p>
 * Basic usage exemple on an enum class called MyEnum:
 *
 *   private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
 *   public static MyEnum failSafeValueOf(String enumName) {
 *       return FAIL_SAFE.valueOf(enumName);
 *   }
 *
 * </p>
 *
 * <p>
 * You can also use it outside of the enum this way:
 *   FailSafeValueOf.create(MyEnum.class).valueOf("EnumName");
 * </p>
 *
 * @author Sebastien Lorber <i>(lorber.sebastien@gmail.com)</i>
 */
public class FailSafeValueOf<T extends Enum<T>> {

    private final Map<String,T> nameToEnumMap;

    private FailSafeValueOf(Class<T> enumClass) {
        Map<String,T> map = Maps.newHashMap();
        for ( T value : EnumSet.allOf(enumClass)) {
            map.put( value.name() , value);
        }
        nameToEnumMap = ImmutableMap.copyOf(map);
    }

    /**
     * Returns the value of the given enum element
     * If the 
     * @param enumName
     * @return
     */
    public T valueOf(String enumName) {
        return nameToEnumMap.get(enumName);
    }

    public static <U extends Enum<U>> FailSafeValueOf<U> create(Class<U> enumClass) {
        return new FailSafeValueOf<U>(enumClass);
    }

}

And the unit test:

import org.testng.annotations.Test;

import static org.testng.Assert.*;


/**
 * @author Sebastien Lorber <i>(lorber.sebastien@gmail.com)</i>
 */
public class FailSafeValueOfTest {

    private enum MyEnum {
        TOTO,
        TATA,
        ;

        private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
        public static MyEnum failSafeValueOf(String enumName) {
            return FAIL_SAFE.valueOf(enumName);
        }
    }

    @Test
    public void testInEnum() {
        assertNotNull( MyEnum.failSafeValueOf("TOTO") );
        assertNotNull( MyEnum.failSafeValueOf("TATA") );
        assertNull( MyEnum.failSafeValueOf("TITI") );
    }

    @Test
    public void testInApp() {
        assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TOTO") );
        assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TATA") );
        assertNull( FailSafeValueOf.create(MyEnum.class).valueOf("TITI") );
    }

}

Notice that i used Guava to make an ImmutableMap but actually you could use a normal map i think since the map is never returned...

Sebastien Lorber
  • 79,294
  • 59
  • 260
  • 386
4

Most of the answers suggest either using a loop with equals to check if the enum exists or using try/catch with enum.valueOf(). I wanted to know which method is faster and tried it. I am not very good at benchmarking, so please correct me if I made any mistakes.

Heres the code of my main class:

    package enumtest;

public class TestMain {

    static long timeCatch, timeIterate;
    static String checkFor;
    static int corrects;

    public static void main(String[] args) {
        timeCatch = 0;
        timeIterate = 0;
        TestingEnum[] enumVals = TestingEnum.values();
        String[] testingStrings = new String[enumVals.length * 5];
        for (int j = 0; j < 10000; j++) {
            for (int i = 0; i < testingStrings.length; i++) {
                if (i % 5 == 0) {
                    testingStrings[i] = enumVals[i / 5].toString();
                } else {
                    testingStrings[i] = "DOES_NOT_EXIST" + i;
                }
            }

            for (String s : testingStrings) {
                checkFor = s;
                if (tryCatch()) {
                    ++corrects;
                }
                if (iterate()) {
                    ++corrects;
                }
            }
        }

        System.out.println(timeCatch / 1000 + "us for try catch");
        System.out.println(timeIterate / 1000 + "us for iterate");
        System.out.println(corrects);
    }

    static boolean tryCatch() {
        long timeStart, timeEnd;
        timeStart = System.nanoTime();
        try {
            TestingEnum.valueOf(checkFor);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        } finally {
            timeEnd = System.nanoTime();
            timeCatch += timeEnd - timeStart;
        }

    }

    static boolean iterate() {
        long timeStart, timeEnd;
        timeStart = System.nanoTime();
        TestingEnum[] values = TestingEnum.values();
        for (TestingEnum v : values) {
            if (v.toString().equals(checkFor)) {
                timeEnd = System.nanoTime();
                timeIterate += timeEnd - timeStart;
                return true;
            }
        }
        timeEnd = System.nanoTime();
        timeIterate += timeEnd - timeStart;
        return false;
    }
}

This means, each methods run 50000 times the lenght of the enum I ran this test multiple times, with 10, 20, 50 and 100 enum constants. Here are the results:

  • 10: try/catch: 760ms | iteration: 62ms
  • 20: try/catch: 1671ms | iteration: 177ms
  • 50: try/catch: 3113ms | iteration: 488ms
  • 100: try/catch: 6834ms | iteration: 1760ms

These results were not exact. When executing it again, there is up to 10% difference in the results, but they are enough to show, that the try/catch method is far less efficient, especially with small enums.

Alexander Daum
  • 732
  • 6
  • 12
4

Since Java 8, we could use streams instead of for loops. Also, it might be apropriate to return an Optional if the enum does not have an instance with such a name.

I have come up with the following three alternatives on how to look up an enum:

private enum Test {
    TEST1, TEST2;

    public Test fromNameOrThrowException(String name) {
        return Arrays.stream(values())
                .filter(e -> e.name().equals(name))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("No enum with name " + name));
    }

    public Test fromNameOrNull(String name) {
        return Arrays.stream(values()).filter(e -> e.name().equals(name)).findFirst().orElse(null);
    }

    public Optional<Test> fromName(String name) {
        return Arrays.stream(values()).filter(e -> e.name().equals(name)).findFirst();
    }
}
Magnilex
  • 10,219
  • 8
  • 49
  • 72
3

Just use valueOf() method. If the value doesn't exist, it throws IllegalArgumentException and you can catch it like that:

      boolean isSettingCodeValid = true;

       try {
            SettingCode.valueOf(settingCode.toUpperCase());
        } catch (IllegalArgumentException e) {
            // throw custom exception or change the isSettingCodeValid value
            isSettingCodeValid = false;
        }
Sarvar Nishonboyev
  • 8,878
  • 7
  • 55
  • 55
1

You can also use Guava and do something like this:

// This method returns enum for a given string if it exists, otherwise it returns default enum.
private MyEnum getMyEnum(String enumName) {
  // It is better to return default instance of enum instead of null
  return hasMyEnum(enumName) ? MyEnum.valueOf(enumName) : MyEnum.DEFAULT;
}

// This method checks that enum for a given string exists.
private boolean hasMyEnum(String enumName) {
  return Iterables.any(Arrays.asList(MyEnum.values()), new Predicate<MyEnum>() {
    public boolean apply(MyEnum myEnum) {
      return myEnum.name().equals(enumName);
    }
  }); 
}

In second method I use guava (Google Guava) library which provides very useful Iterables class. Using the Iterables.any() method we can check if a given value exists in a list object. This method needs two parameters: a list and Predicate object. First I used Arrays.asList() method to create a list with all enums. After that I created new Predicate object which is used to check if a given element (enum in our case) satisfies the condition in apply method. If that happens, method Iterables.any() returns true value.

Dawid Stępień
  • 1,039
  • 7
  • 6