【问题标题】:Java: Check if enum contains a given string?Java:检查枚举是否包含给定的字符串?
【发布时间】:2011-06-23 15:16:25
【问题描述】:

这是我的问题 - 我正在寻找(如果它存在的话)ArrayList.contains(); 的枚举等价物。

这是我的代码问题示例:

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

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

现在,我意识到StringsArrayList 将是更好的路线,但我必须通过其他地方的开关/案例来运行我的枚举内容。这就是我的问题。

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

【问题讨论】:

  • 从 Java 7 开始实现带字符串的切换/大小写

标签: java string enums


【解决方案1】:

改用 Apache commons lang3 lib

 EnumUtils.isValidEnum(MyEnum.class, myValue)

【讨论】:

  • 请注意,对于那些感兴趣的人,他们使用的底层实现只是 try/catch 解决方案(@since 3.0 @version $Id: EnumUtils.java 1199894 2011-11-09 17:53:59Z ggregory $)。
  • 使我的代码更简单,所以我不在乎它是否使用异常来进行流控制(他们真的不应该)......如果他们改变了那会很好。
  • 番石榴也包含这样的解决方案吗?
  • 此时,这应该是公认的答案:-)。
【解决方案2】:

应该这样做:

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") 返回真或假。

【讨论】:

  • 这是一个非常糟糕的实现:Choice.valueOf(test) 是你想要的(w/try/catch)
  • bestsss,这显然是最合适的解决方案。抛出异常来实现一种 exists() 方法是不好的做法。虽然您可能认为您的实现效率更高,因为它看起来不是 O(n),但它位于不可见的底层框架中。还使用 try {} catch 会增加开销。另外,它只是不漂亮。
  • @Jared,绝对值。只需捕获您的异常并返回 false。对于那些不这样说的人,如果你看一下实现,它已经使用了 Map,JDK 开发人员有更好的机会来优化它。 API 抛出异常,这是一种值得商榷的做法(而不是返回 null),但是当您处理抛出异常的 API 时,请顺其自然,不要重新发明轮子。
  • @jluzwick try/catch 开销是一个简单的跳转指令,当不被占用时,在catch块中不使用Exception也得到了优化。担心 try/catch 会导致性能下降是一种不好的做法。
  • contains() 优先于 valueOf() 有一个例外。为什么?因为“异常,顾名思义,仅用于异常情况;它们绝不应该用于普通控制流”(Joshua Bloch,“Effective Java”)。
【解决方案3】:

您可以使用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
  }
}

如果您希望检查经常失败,您最好使用一个简单的循环,如其他人所示 - 如果您的枚举包含 许多 值,也许构建 HashSet 或类似的枚举值转换为字符串并改为查询 HashSet

【讨论】:

  • 我不认为 Exception 在这种情况下是最好的选择。
  • Try and Catch 应该是最后的手段。 Try and Catch 太贵了
  • 依赖运行时异常来执行业务逻辑,除了成本高昂之外,可读性也不高。关于检查的异常,情况有所不同,因为它们是业务的一部分。
  • 这也可以防止任何人打开广泛中断抛出的异常以查找正在重试的实际异常情况。 (或者至少让这样做很烦人)。对例外情况使用例外。
  • EnumUtils.isValidEnum(MyEnum.class, myValue) 使用类似的逻辑,IMO 为这个琐碎的任务添加整个库确实有意义
【解决方案4】:

如果您使用的是 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);

【讨论】:

  • 在性能方面,它与@richard-h 的 for-loop 解决方案存在相同的问题。
【解决方案5】:

Guavas Enums 可能是你的朋友

例如这个:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

【讨论】:

    【解决方案6】:

    更好:

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

    【讨论】:

    • 感谢乐观的解决方案
    【解决方案7】:

    你可以先将枚举转成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。
    【解决方案8】:

    这里提到了几个库,但我错过了我真正要寻找的一个:Spring!

    ObjectUtils#containsConstant 默认情况下不区分大小写,但如果您愿意,也可以很严格。它是这样使用的:

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

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

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

    作为奖励,它还提供了 valueOf 的不区分大小写的变体:ObjectUtils#caseInsensitiveValueOf

    【讨论】:

      【解决方案9】:

      几个假设:
      1) 没有 try/catch,因为它是特殊的流量控制
      2) 'contains' 方法必须快速,因为它通常会运行多次。
      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);
          }
      }
      

      【讨论】:

        【解决方案10】:

        你可以用这个

        你的枚举 {A1, A2, B1, B2}

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

        合并了@wightwulf1944 建议的更新,以提高解决方案的效率。

        【讨论】:

        • 这种实现效率低下。这将遍历枚举值以创建一个新集,然后创建一个流,该流遍历结果集。这意味着每次调用函数时都会创建一个新的 Set 和 Stream ,并且在集合上使用 stream() 意味着您正在迭代集合中的每个元素,而不是利用更快的底层哈希表。为了改善这一点,最好缓存创建的 Set 并改用它的 contains() 方法。如果您必须获取流,请改用Arrays.stream()
        • @SubaruTashiro 但是哪个库包含Sets?这是一个不错的解决方案,但不适用于纯 Java。
        • 标准库中的 @BairDev java.util.HashSet&lt;E&gt; 可以正常工作。 Sets 是不必要的。
        【解决方案11】:

        Java Streams 提供了优雅的方式来做到这一点

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

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

        【讨论】:

          【解决方案12】:

          我不认为有,但你可以这样做:

          enum choices {a1, a2, b1, b2};
          
          public static boolean exists(choices choice) {
             for(choice aChoice : choices.values()) {
                if(aChoice == choice) {
                   return true;
                }
             }
             return false;
          }
          

          编辑:

          请参阅 Richards 的版本,因为它更合适,除非您将其转换为使用字符串,Richards 会这样做。

          【讨论】:

          • 但我认为 OP 想要测试一个字符串?
          • 是的,哈哈。这种方法不会那么有效,因为我们已经知道选择在枚举中。你的修改更正确。
          • 如果您想处理枚举本身的某些子集(而不是它们的名称),最好查看 EnumSet。 download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
          • 也许是个愚蠢的问题,但为什么 .values() 没有记录在 download.oracle.com/javase/6/docs/api/java/lang/Enum.html 中?
          • 这是个好问题。你是对的,它不存在于文档中,它也不存在于 Enum 源中。我假设它存在于 Enum 的一种实现中,或者有一些 JLS 规则允许它。此外,所有 Collection 对象都有这个,即使它不一定实现 Collection,也可以将其视为一个 Collection。
          【解决方案13】:

          为什么不将 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;
                  }
          }
          

          【讨论】:

          【解决方案14】:

          我只想写,

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

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

          【讨论】:

          • 谢谢。我正在使用的枚举有一种非常规的方式来获取枚举的字符串值,所以我不得不像这样修改你的答案: Arrays.stream(EventNames.values()).map(EventNames::getEvent) .collect (Collectors.toList()).contains(aString); EventNames 是枚举的名称,而 getEvent() 是返回每个枚举成员的关联字符串值。
          • @MattCampbell 如果可行,那么点赞会很有帮助。
          【解决方案15】:

          此方法可用于检查任何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");
          

          【讨论】:

            【解决方案16】:

            这个对我有用:

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

            【讨论】:

            • 即使 YourEnum 包含“valueToCheckBlaBla”,您的版本也会返回 true,因为“valueToCheck”将出现在整个列表的字符串表示中。
            【解决方案17】:

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

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

            【讨论】:

              【解决方案18】:

              如果你使用的是 Java 8 或更高版本,你可以这样做:

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

              【讨论】:

                【解决方案19】:

                你可以把它做成 contains 方法:

                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
                }
                
                

                【讨论】:

                  【解决方案20】:

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

                  【讨论】:

                  • 但这并不优雅..如果该值不存在,则会引发异常。所以你可能必须用 try catch 包围它
                  • 如果值不存在会抛出异常
                  • 比遍历枚举选项寻找匹配对象更不优雅?
                  【解决方案21】:

                  它是一个枚举,那些是常量值,所以如果它在一个 switch 语句中它只是做这样的事情:

                  case: val1
                  case: val2
                  

                  还有为什么你需要知道什么被声明为常量?

                  【讨论】:

                  • 这段代码不在上述 switch 语句中,在别处。我只是说在这种情况下,枚举是必要的,因为其他人建议我使用 ArrayList 代替。
                  • @Jared 现在更有意义了
                  • @Jared 但是这并不重要,因为您已经知道那里的值。基本上 list.contains() 的枚举等价物是 MyEnum.MyAwesomeValue
                  【解决方案22】:

                  使用番石榴就更简单了:

                  boolean isPartOfMyEnum(String myString){
                  
                  return Lists.newArrayList(MyEnum.values().toString()).contains(myString);
                  
                  }
                  

                  【讨论】:

                  • 正如 kioria 所说,这行不通。 MyEnum.values() 返回 MyEnum 实例的数组,MyEnum.value().toString() 返回此数组对象的字符串表示形式(就像“[LMyEnum;@15b94ed3”这样的字符串)
                  • 你需要调用.name()而不是.toString()(除非你覆盖了默认的toString方法)。有关更多信息,请参阅此内容:Difference between enum .name() and .toString()
                  【解决方案23】:

                  这结合了以前方法中的所有方法,并且应该具有相同的性能。它可以用于任何枚举,内联来自@Richard H 的“编辑”解决方案,并对@bestsss 等无效值使用异常。唯一的折衷是需要指定类,但这会将其变成两行。

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

                  }

                  【讨论】:

                    【解决方案24】:
                    com.google.common.collect.Sets.newHashSet(MyEnum.values()).contains("myValue")
                    

                    【讨论】:

                      【解决方案25】:

                      检查值是否存在以及返回枚举值的解决方案:

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

                      【讨论】:

                        【解决方案26】:
                          Set.of(CustomType.values())
                             .contains(customTypevalue) 
                        

                        【讨论】:

                          【解决方案27】:

                          你也可以使用:com.google.common.base.Enums

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

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

                          【讨论】:

                            【解决方案28】:

                            EnumUtils.isValidEnum 如果你想导入 Apache commons lang3 可能是最好的选择。如果不关注是一个通用函数,我会使用它作为替代。

                            private <T extends Enum<T>> boolean enumValueExists(Class<T> enumType, String value) {
                                boolean result;
                                try {
                                    Enum.valueOf(enumType, value);
                                    result = true;
                                } catch (IllegalArgumentException e) {
                                    result = false;
                                }
                                return result;
                            }
                            
                            

                            并如下使用它

                            if (enumValueExists(MyEnum.class, configValue)) {
                                // happy code
                            } else {
                                // angry code
                            }
                            

                            【讨论】:

                              【解决方案29】:

                              虽然迭代列表或捕获异常对于大多数情况来说已经足够了,但我正在寻找适用于大型枚举的可重用的东西。最后,我最终写了这个:

                              public class EnumValidator {
                              
                                  private final Set<String> values;
                              
                                  private EnumValidator(Set<String> values) {
                                      this.values = values;
                                  }
                              
                                  public static <T extends Enum<T>> EnumValidator of(Class<T> enumType){
                                      return new EnumValidator(Stream.of(enumType.getEnumConstants()).map(Enum::name).collect(Collectors.toSet()));
                                  }
                              
                                  public boolean isPresent(String et){
                                      return values.contains(et);
                                  }
                                  
                              }
                              

                              【讨论】:

                                【解决方案30】:

                                Java 8+ 流+设置方式:

                                    // Build the set.
                                    final Set<String> mySet = Arrays//
                                      .stream(YourEnumHere.values())//
                                      .map(Enum::name)//
                                      .collect(Collectors.toSet());
                                
                                    // Reuse the set for contains multiple times.
                                    mySet.contains(textA);
                                    mySet.contains(textB);
                                    ...
                                

                                【讨论】:

                                  猜你喜欢
                                  • 1970-01-01
                                  • 2014-08-09
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2012-08-30
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2017-03-28
                                  相关资源
                                  最近更新 更多