检查枚举是否存在于 Java 中

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1167982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:28:17  来源:igfitidea点击:

Check if enum exists in Java

javastringenumscompare

提问by Danny

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 valueOfmethod and catch an exception but I'v been taught that catching runtime exceptions is not good practice. Anybody have any ideas?

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

采纳答案by Michael Myers

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.

编辑:正如 Jon Skeet 所指出的,values()每次调用时都会克隆一个私有后备数组。如果性能很重要,您可能只想调用values()一次,缓存数组,然后遍历它。

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

此外,如果您的枚举具有大量值,则 Jon Skeet 的 map 替代方案可能比任何数组迭代执行得更好。

回答by nos

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

我不知道为什么有人告诉你捕获运行时异常很糟糕。

Use valueOfand catching IllegalArgumentExceptionis fine for converting/checking a string to an enum.

使用valueOf和捕获IllegalArgumentException非常适合将字符串转换/检查为枚举。

回答by Jon Skeet

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.

如果我需要这样做,我有时会构建一个Set<String>名称,甚至是我自己的名称Map<String,MyEnum>- 那么您可以检查一下。

A couple of points worth noting:

有几点值得注意:

  • Populate any such static collection in a static initializer. Don'tuse 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.allOfwhich is much more efficient for enums without a large number of elements.
  • 在静态初始值设定项中填充任何此类静态集合。不要使用变量初始值设定项,然后依赖它在枚举构造函数运行时已执行 - 它不会!(枚举构造函数是在静态初始化器之前首先执行的事情。)
  • 尽量避免values()频繁使用- 它每次都必须创建并填充一个新数组。要迭代所有元素,请使用EnumSet.allOf对于没有大量元素的枚举更有效。

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:

当然,如果你只有几个名字,这可能有点矫枉过正——当 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"));
    }
}

回答by Sebastien Lorber

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

根据 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>([email protected])</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>([email protected])</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...

请注意,我使用 Guava 制作了一个 ImmutableMap 但实际上你可以使用我认为的法线贴图,因为该贴图永远不会返回......

回答by рüффп

One of my favorite lib: Apache Commons.

我最喜欢的库之一:Apache Commons。

The EnumUtilscan do that easily.

EnumUtils能做到这一点很容易。

Following an example to validate an Enum with that library:

以下是使用该库验证 Enum 的示例:

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);
}

回答by Dawid St?pień

You can also use Guava and do something like this:

您还可以使用 Guava 并执行以下操作:

// 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 Iterablesclass. 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 Predicateobject. First I used Arrays.asList()method to create a list with all enums. After that I created new Predicateobject which is used to check if a given element (enum in our case) satisfies the condition in applymethod. If that happens, method Iterables.any()returns true value.

在第二种方法中,我使用 guava ( Google Guava) 库,它提供了非常有用的Iterables类。使用Iterables.any()方法,我们可以检查给定值是否存在于列表对象中。该方法需要两个参数:一个列表和Predicate对象。首先,我使用Arrays.asList()方法创建一个包含所有枚举的列表。之后我创建了新的Predicate对象,用于检查给定元素(在我们的例子中是枚举)是否满足apply方法中的条件。如果发生这种情况,方法Iterables.any()将返回真值。

回答by Alexander Daum

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.

大多数答案建议要么使用带有 equals 的循环来检查枚举是否存在,要么使用带有 enum.valueOf() 的 try/catch。我想知道哪种方法更快并尝试了它。我不是很擅长基准测试,所以如果我犯了任何错误,请纠正我。

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:

这意味着,每个方法运行的枚举长度是我多次运行此测试的枚举长度的 50000 倍,使用 10、20、50 和 100 个枚举常量。结果如下:

  • 10: try/catch: 760ms | iteration: 62ms
  • 20: try/catch: 1671ms | iteration: 177ms
  • 50: try/catch: 3113ms | iteration: 488ms
  • 100: try/catch: 6834ms | iteration: 1760ms
  • 10:尝试/捕获:760ms | 迭代:62ms
  • 20:尝试/捕获:1671ms | 迭代:177ms
  • 50:尝试/捕获:3113ms | 迭代:488ms
  • 100:尝试/捕获:6834ms | 迭代: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.

这些结果并不准确。再次执行时,结果有高达 10% 的差异,但它们足以表明 try/catch 方法的效率要低得多,尤其是对于小枚举。

回答by Magnilex

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

从 Java 8 开始,我们可以使用代替 for 循环。此外,如果枚举没有具有此类名称的实例,则返回Optional可能是合适的。

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();
    }
}

回答by Sarvar Nishonboev

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

只需使用 valueOf() 方法。如果该值不存在,它会抛出 IllegalArgumentException 并且您可以像这样捕获它:

      boolean isSettingCodeValid = true;

       try {
            SettingCode.valueOf(settingCode.toUpperCase());
        } catch (IllegalArgumentException e) {
            // throw custom exception or change the isSettingCodeValid value
            isSettingCodeValid = false;
        }