检查Java中是否存在枚举
反正有没有通过比较给定的string来检查一个枚举是否存在? 我似乎无法find任何这样的function。 我可以尝试使用valueOf
方法并捕获一个exception,但我被告知捕获运行时exception并不是好的做法。 任何人有任何想法?
我不认为有一个内置的方法来做到这一点,而不捕捉exception。 你可以改用这样的东西:
public static MyEnum asMyEnum(String str) { for (MyEnum me : MyEnum.values()) { if (me.name().equalsIgnoreCase(str)) return me; } return null; }
编辑:正如Jon Skeet指出的, values()
每次调用时都会克隆一个私有的后备数组。 如果性能至关重要,您可能只需要调用一次values()
,caching数组,然后遍历它。
另外,如果你的枚举值有很多,那么Jon Skeet的map替代方法可能比任何数组迭代都要好。
如果我需要这样做,我有时会build立一个Set<String>
的名字,甚至是我自己的Map<String,MyEnum>
– 然后你可以检查它。
值得注意的几点:
- 在静态初始化器中填充任何这样的静态集合。 不要使用variables初始值设定项,然后在枚举构造函数运行时依赖它被执行 – 它不会是! (枚举构造函数是在静态初始化之前要执行的第一件事情。)
- 尽量避免频繁使用
values()
– 它必须每次都创build并填充一个新的数组。 要迭代所有元素,请使用EnumSet.allOf
,对于没有大量元素的枚举,效率要高得多。
示例代码:
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")); } }
当然,如果你只有几个名字,这可能是过度的 – 当n足够小的时候,O(n)解决scheme常常胜过O(1)解决scheme。 这是另一种方法:
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")); } }
我最喜欢的lib之一:Apache Commons。
EnumUtils可以轻松完成。
以下是validationEnum的示例代码:
MyEnum strTypeEnum = null; // test if String str is compatible with the enum if( EnumUtils.isValidEnum(MyEnum.class, str) ){ strTypeEnum = MyEnum.valueOf(str); }
我不知道为什么有人告诉你捕捉运行时exception是不好的。
使用valueOf
和捕捉IllegalArgumentException
是很好的转换/检查一个string枚举。
基于Jon Skeet的回答,我做了一个允许在工作中轻松完成的课程:
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); } }
而unit testing:
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") ); } }
请注意,我使用番石榴做一个ImmutableMap,但实际上你可以使用一个普通的地图,我认为,因为地图永远不会返回…
你也可以使用番石榴,做这样的事情:
// 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); } }); }
在第二种方法中,我使用番石榴( 谷歌番石榴 )库,它提供了非常有用的Iterables类。 使用Iterables.any()方法,我们可以检查给定值是否存在于列表对象中。 这个方法需要两个参数:一个列表和Predicate对象。 首先我使用Arrays.asList()方法创build一个包含所有枚举的列表。 之后,我创build了一个新的Predicate对象,用来检查给定的元素(在我们的例子中是枚举)是否满足apply方法中的条件。 如果发生这种情况,方法Iterables.any()返回true值。
大多数的答案build议使用等于的循环检查是否存在枚举或使用try / catch与enum.valueOf()。 我想知道哪种方法更快,并尝试过。 我不擅长基准testing,所以如果我犯了错误,请纠正我的错误。
下面是我的主要类的代码:
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; } }
这意味着,每个方法运行的枚举的长度是我运行此testing的长度的50000倍,具有10,20,50和100个枚举常量。 结果如下:
- 10:try / catch:760ms | 迭代:62ms
- 20:try / catch:1671ms | 迭代:177ms
- 50:try / catch:3113ms | 迭代:488ms
- 100:try / catch:6834ms | 迭代:1760ms
这些结果并不准确。 当再次执行时,结果的差异高达10%,但足以certificatetry / catch方法效率低下,尤其是对于小的枚举。