Java:检查枚举是否包含给定的字符串?


169

这是我的问题-我正在寻找(如果存在的话)相当于 ArrayList.contains();

这是我的代码问题的一个示例:

enum choices {a1, a2, b1, b2};

if(choices.???(a1)}{
//do this
} 

现在,我意识到,使用ArrayListof Strings会是更好的方法,但是我必须通过其他地方的开关/案例来运行我的枚举内容。因此,我的问题。

假设不存在这样的事情,我该怎么做呢?


从Java 7
AndreyP

Answers:


205

应该这样做:

public static boolean contains(String test) {

    for (Choice c : Choice.values()) {
        if (c.name().equals(test)) {
            return true;
        }
    }

    return false;
}

这样,您不必担心以后再添加其他枚举值,它们都已选中。

编辑:如果枚举很大,则可以将值粘贴在HashSet中:

public static HashSet<String> getEnums() {

  HashSet<String> values = new HashSet<String>();

  for (Choice c : Choice.values()) {
      values.add(c.name());
  }

  return values;
}

然后,你可以这样做:values.contains("your string")返回true或false。


12
那是一个非常差的暗示。:Choice.valueOf(test)是您想要的(带有try / catch)
bestsss 2011-2-8

17
最好,这显然是最合适的解决方案。抛出异常来实现现存类型的方法是错误的做法。虽然您可能会因为看不到O(n)而认为自己的实现效率更高,但是它在底层框架中是不可见的。另外,使用try {} catch会增加开销。另外,它不是很漂亮。
jluzwick 2011年

25
@Jared,绝对是valueOf。只是捕获您的异常并返回false。对于那些不这样说的人,如果您看一下实现,它已经使用了Map,并且JDK开发人员有更好的机会对此进行优化。API会引发异常,这是值得商practice的做法(而不是返回null),但是当您处理引发异常的API时,请不要乱动。
Yishai

2
@jluzwick的try / catch开销是不使用时的简单跳转指令,在catch块中不使用Exception的情况也得到了优化。害怕尝试/捕获导致性能下降的原因是一个坏习惯。
bestsss 2011年

22
有例外,contains()优先于valueOf()。为什么?因为“异常,顾名思义,只能用于特殊情况;绝不能用于常规控制流”(Joshua Bloch,“有效Java”)。
james.garriss 2014年

226

改用Apache commons lang3 lib

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
请注意,对于那些感兴趣的人,他们使用的底层实现只是try / catch解决方案(@since 3.0 @version $ Id:EnumUtils.java 1199894 2011-11-09 17:53:59Z ggregory $)。
Jonathan Gawrych

1
使我的代码更简单,所以我不在乎它是否将异常用于流控制(它们实际上不应该)...如果他们更改了它,那将很好。
jpangamarca


1
番石榴还包含这样的解决方案吗?
赛普拉斯弗兰肯费尔德

50

您可以使用 Enum.valueOf()

enum Choices{A1, A2, B1, B2};

public class MainClass {
  public static void main(String args[]) {
    Choices day;

    try {
       day = Choices.valueOf("A1");
       //yes
    } catch (IllegalArgumentException ex) {  
        //nope
  }
}

如果您希望检查经常失败,那么最好使用其他循环所示的简单循环-如果您的枚举包含许多值,则可能将builda HashSet或类似的枚举值转换为字符串并进行查询HashSet


8
我不认为在这种情况下Exception是最佳选择。
GokcenG 2014年

6
尝试和抓住应该是最后的选择。尝试和捉住都是昂贵的
耶稣·迪姆里克斯(Demrix)2014年

2
除了昂贵之外,依靠运行时异常来执行业务逻辑还不那么容易理解。关于检查异常,这是不同的,因为这些异常是业务的一部分。
路易斯Soares的

2
这也可以防止任何人对抛出的异常进行大范围的中断,以查找正在重试的实际例外情况。(或至少使它非常烦人)。在例外情况下使用例外。
Nickolay Kondratyev

4
EnumUtils.isValidEnum(MyEnum.class, myValue)使用类似的逻辑和IMO,为这个琐碎的任务添加一个完整的库确实很有意义
Vikramjit Roy

37

如果您使用的是Java 1.8,则可以选择Stream + Lambda来实现此目的:

public enum Period {
    DAILY, WEEKLY
};

//This is recommended
Arrays.stream(Period.values()).anyMatch((t) -> t.name().equals("DAILY1"));
//May throw java.lang.IllegalArgumentException
Arrays.stream(Period.values()).anyMatch(Period.valueOf("DAILY")::equals);

19

更好的是:

enum choices {
   a1, a2, b1, b2;

  public static boolean contains(String s)
  {
      for(choices choice:values())
           if (choice.name().equals(s)) 
              return true;
      return false;
  } 

};

感谢您的乐观解决方案
Parth Patel

19

番石榴枚举可能是你的朋友

像这样:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

    if (!Enums.getIfPresent(MyData.class, "THREE").isPresent()) {
        System.out.println("THREE is not here");
    }
}

11

您可以先将枚举转换为List,然后使用list contains方法

enum Choices{A1, A2, B1, B2};

List choices = Arrays.asList(Choices.values());

//compare with enum value 
if(choices.contains(Choices.A1)){
   //do something
}

//compare with String value
if(choices.contains(Choices.valueOf("A1"))){
   //do something
}

这应该是公认的答案。转换为列表是最干净的方法。在这里的其他答案中,它保留了Java中有关“正确使用异常”的整个(副)讨论。
曼努埃尔

如果该值不存在,则抛出IllegalArgumentException。
mkyong

10

这里提到了几个库,但是我想念我真正想要的一个库:春天!

有一个ObjectUtils#containsConstant,默认情况下不区分大小写,但如果需要,可以严格。它的用法如下:

if(ObjectUtils.containsConstant(Choices.values(), "SOME_CHOISE", true)){
// do stuff
}

注意:我在这里使用了重载方法来演示如何使用区分大小写的检查。您可以忽略布尔值以使其具有不区分大小写的行为。

但是请注意大型枚举,因为它们不会像某些枚举那样使用Map实现...

另外,它还提供了valueOf的不区分大小写的变体:ObjectUtils#caseInsensitiveValueOf


9

很少有以下假设:
1)无需尝试/捕获,因为它是出色的流量控制
2)“包含”方法必须快速运行,因为它通常运行多次。
3)空间不受限制(普通解决方案常见)

import java.util.HashSet;
import java.util.Set;

enum Choices {
    a1, a2, b1, b2;

    private static Set<String> _values = new HashSet<>();

    // O(n) - runs once
    static{
        for (Choices choice : Choices.values()) {
            _values.add(choice.name());
        }
    }

    // O(1) - runs several times
    public static boolean contains(String value){
        return _values.contains(value);
    }
}

7

你可以用这个

YourEnum {A1,A2,B1,B2}

boolean contains(String str){ 
    return Sets.newHashSet(YourEnum.values()).contains(str);
}                                  

@ wightwulf1944建议的更新已合并,以使解决方案更有效。


4
这种实施方式效率低下。这将遍历枚举值以创建一个新集合,然后创建一个流,该流将遍历结果集。这意味着每次调用该函数时都会创建一个新的Set and Stream,并且stream()在set上使用意味着您要对set中的每个元素进行迭代,而不是利用将更快的基础哈希表。为了改善这一点,最好缓存创建的Set并改用它的contains()方法。如果必须获取流,请Arrays.stream()改用。
斯巴鲁Tashiro

3

我认为没有,但是您可以执行以下操作:

enum choices {a1, a2, b1, b2};

public static boolean exists(choices choice) {
   for(choice aChoice : choices.values()) {
      if(aChoice == choice) {
         return true;
      }
   }
   return false;
}

编辑:

请查看Richard的版本,因为它更合适,因为除非将其转换为使用Richards的Strings,否则它将不起作用。


但我认为OP想要测试字符串?
理查德H

是的,哈哈 因为我们已经知道选择在枚举中,所以这种方法不会那么有效。您的修改更正确。
jluzwick 2011年

1
如果要处理枚举本身的某些子集(而不是其名称),则最好查看EnumSet。download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai

也许是一个愚蠢的问题,但是为什么没有下载.values()文件download.oracle.com/javase/6/docs/api/java/lang/Enum.html
匿名

这是一个很好的问题。您是对的,它在文档中也不存在,在Enum源代码中也不存在。我假设它存在于Enum的一种实现中,或者有一些允许它的JLS规则。同样,所有Collection对象都具有此功能,即使它不一定实现Collection,也可以将其视为Collection。
jluzwick 2011年

3

Java Streams提供了一种优雅的方法

Stream.of(MyEnum.values()).anyMatch(v -> v.name().equals(strValue))

返回:如果流中的任何元素与提供的值匹配,则返回true,否则返回false


2

为什么不将Pablo的回复与valueOf()结合起来?

public enum Choices
{
    a1, a2, b1, b2;

    public static boolean contains(String s) {
        try {
            Choices.valueOf(s);
            return true;
        } catch (Exception e) {
            return false;
        }
}

请不要 参见与您的答案相同的其他更旧的答案:stackoverflow.com/a/4936872/103412
Torsten

1

此方法可用于检查任何Enum,您可以将其添加到Utils类中:

public static <T extends Enum<T>> boolean enumContains(Class<T> enumerator, String value)
{
    for (T c : enumerator.getEnumConstants()) {
        if (c.name().equals(value)) {
            return true;
        }
    }
    return false;
}

使用这种方式:

boolean isContained = Utils.enumContains(choices.class, "value");

1

我为此验证创建了下一个类

public class EnumUtils {

    public static boolean isPresent(Enum enumArray[], String name) {
        for (Enum element: enumArray ) {
            if(element.toString().equals(name))
                return true;
        }
        return false;
    }

}

使用示例:

public ArrivalEnum findArrivalEnum(String name) {

    if (!EnumUtils.isPresent(ArrivalEnum.values(), name))
        throw new EnumConstantNotPresentException(ArrivalEnum.class,"Arrival value must be 'FROM_AIRPORT' or 'TO_AIRPORT' ");

    return ArrivalEnum.valueOf(name);
}

0

valueOf("a1")如果要按字符串查找,可以使用


3
但这不是很好。如果该值不存在,它将引发异常。因此,您可能必须尝试一下才能包围它
Kasturi

如果该值不存在,则将引发异常
Richard H

不像遍历枚举选择匹配对象那样优雅?
jprete 2011年

0

它是一个枚举,它们是常量值,因此,如果在switch语句中执行以下操作:

case: val1
case: val2

另外,为什么还要知道什么是常量?


这段代码不在所说的switch语句中,而是在其他地方。我只是说在这种情况下,枚举是必要的,因为其他人建议我改用ArrayList。
贾里德

@Jared现在变得更有意义了
Woot4Moo 2011年

@Jared但这并不重要,因为您已经知道其中的值。基本上,list.contains()的枚举等效为MyEnum.MyAwesomeValue
Woot4Moo 2011年

0

使用番石榴,它甚至更简单:

boolean isPartOfMyEnum(String myString){

return Lists.newArrayList(MyEnum.values().toString()).contains(myString);

}

正如kioria所说,这行不通。MyEnum.values()返回MyEnum实例的数组并MyEnum.value().toString()返回此数组对象的字符串表示形式(只是字符串,例如“ [LMyEnum; @ 15b94ed3”)
user2137020 2014年

您需要调用.name()而不是.toString()(除非覆盖默认的toString方法)。看到更多信息:枚举。名称()和的ToString()之间的区别
Gaʀʀʏ

0

这结合了以前方法的所有方法,并且应具有同等的性能。它可以用于任何枚举,内联@Richard H的“编辑”解决方案,并对@bestsss等无效值使用Exceptions。唯一的权衡是需要指定类,但这使它变成了两层。

import java.util.EnumSet;

public class HelloWorld {

static enum Choices {a1, a2, b1, b2}

public static <E extends Enum<E>> boolean contains(Class<E> _enumClass, String value) {
    try {
        return EnumSet.allOf(_enumClass).contains(Enum.valueOf(_enumClass, value));    
    } catch (Exception e) {
        return false; 
    }
}

public static void main(String[] args) {
    for (String value : new String[] {"a1", "a3", null}) {
        System.out.println(contains(Choices.class, value));
    }
}

}


0
com.google.common.collect.Sets.newHashSet(MyEnum.values()).contains("myValue")

0

检查值是否存在的解决方案以及获取枚举值作为回报:

protected TradeType getEnumType(String tradeType) {
    if (tradeType != null) {
        if (EnumUtils.isValidEnum(TradeType.class, tradeType)) {
            return TradeType.valueOf(tradeType);
        }
    }
    return null;
}

0

这对我有用:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");

3
即使YourEnum包含“ valueToCheckBlaBla”,您的版本也将返回true,因为“ valueToCheck”将出现在整个列表的字符串表示中。
Nicko '18年

0

如果您使用的是Java 8或更高版本,则可以执行以下操作:

boolean isPresent(String testString){
      return Stream.of(Choices.values()).map(Enum::name).collect(Collectors.toSet()).contains(testString);
}


0

您可以将其作为包含方法:

enum choices {a1, a2, b1, b2};
public boolean contains(String value){
    try{
        EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, value));
        return true;
    }catch (Exception e) {
        return false;
    }
}

或者您可以将其与代码块一起使用:

try{
    EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, "a1"));
    //do something
}catch (Exception e) {
    //do something else
}

0

您还可以使用:com.google.common.base.Enums

Enums.getIfPresent(varEnum.class,varToLookFor)返回一个可选

Enums.getIfPresent(fooEnum.class,myVariable).isPresent()吗?Enums.getIfPresent(fooEnum.class,myVariable).get:fooEnum.OTHERS


0

我会写,

Arrays.stream(Choice.values()).map(Enum::name).collect(Collectors.toList()).contains("a1");

Enum#equals仅适用于对象比较。


-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}

那行不通。设置有枚举对象,而您正在检查它的字符串。
iTake

现在答案不适合这个问题,因为它与String有关:)
iTake

-11

enum在Java中非常强大。您可以轻松地将contains方法添加到枚举(就像您将方法添加到类一样):

enum choices {
  a1, a2, b1, b2;

  public boolean contains(String s)
  {
      if (s.equals("a1") || s.equals("a2") || s.equals("b1") || s.equals("b2")) 
         return true;
      return false;
  } 

};

你是说 s.equals("b1") || s.equals("b2")吗?
Jigar Joshi

3
这可能不是最好的方法,因为您以后必须s.equals("xx")为每个枚举添加新的枚举。
jluzwick 2011年

1
将会有1000多个枚举。
贾里德

20
提出诸如此类的可怕解决方案的人如何获得64K声誉?我讨厌想到所有基于该贡献者答案的糟糕代码
Dexygen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.