检查Java中是否存在枚举

无论如何,通过将枚举与给定的字符串进行比较来检查枚举是否存在?我似乎无法找到任何这样的功能。我可以尝试使用valueOf方法并捕获一个异常,但我被告知捕获运行时异常并不是一种好的做法。任何人有任何想法?

已邀请:

aut_ea

赞同来自:

我不认为有一种内置的方法可以在不捕捉异常的情况下执行此操作。你可以改用这样的东西:

public static MyEnum asMyEnum(String str) {
    for (MyEnum me : MyEnum.values()) {
        if (me.name().equalsIgnoreCase(str))
            return me;
    }
    return null;
}
编辑:正如Jon Skeet所说,values()每次调用时都会克隆一个私有的后备数组。如果性能至关重要,则可能只需调用一次values(),缓存该数组,然后遍历该数组。 另外,如果你的枚举值有很多值,那么Jon Skeet的map替代方法可能比任何数组迭代都要好。

yeum

赞同来自:

我不知道为什么有人告诉你捕捉运行时异常是不好的。 使用valueOf和捕获IllegalArgumentException对于将字符串转换/检查为枚举是很好的。

tet

赞同来自:

如果我需要这样做,我有时会建立名称的Set<String>,甚至是我自己的Map<String,MyEnum> - 然后您可以检查该名称。 值得注意的几点:

  • 在静态初始化器中填充任何这样的静态集合。不要使用变量初始值设定项,然后依赖它在枚举构造函数运行时执行 - 它不会是! (枚举构造函数是在静态初始化器之前要执行的第一件事。)
  • 尽量避免频繁使用values() - 它必须每次创建并填充一个新数组。要迭代所有元素,请使用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)解决方案常常胜过O(1)解决方案。这是另一种方法:
    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"));
        }
    }
    

bnemo

赞同来自:

基于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);
    }
}
单元测试:
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") );
    }
}
请注意,我使用Guava制作ImmutableMap,但实际上您可以使用普通地图,因为地图永远不会返回。

get

赞同来自:

我最喜欢的lib之一:Apache Commons。 EnumUtils可以轻松完成。 以下是验证Enum的示例代码:

MyEnum strTypeEnum = null;
// test if String str is compatible with the enum 
if( EnumUtils.isValidEnum(MyEnum.class, str) ){
    strTypeEnum = MyEnum.valueOf(str);
}

lenim

赞同来自:

你也可以使用番石榴并做这样的事情:

// 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类的番石榴(Google Guava)库。使用Iterables.any()方法,我们可以检查给定值是否存在于列表对象中。该方法需要两个参数:一个列表和Predicate对象。首先我使用Arrays.asList()方法创建一个包含所有枚举的列表。之后,我创建了新的Predicate对象,用于检查给定元素(在我们的例子中是枚举)是否满足apply方法中的条件。如果发生这种情况,方法Iterables.any()会返回true值。

ueos

赞同来自:

大部分答案都建议使用等于的循环来检查枚举是否存在,或者使用带enum.valueOf()的try/catch。我想知道哪种方法更快,并尝试过。我不擅长基准测试,所以如果我犯了错误,请纠正我。 下面是我主要课程的代码:

    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;
    }
}
这意味着,每个方法运行枚举的长度的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%,但它们足以证明try/catch方法效率低得多,特别是对于小枚举。

要回复问题请先登录注册