题 按字符串值查找Java枚举


说我有一个只是的枚举

public enum Blah {
    A, B, C, D
}

我想找到一个字符串的枚举值,例如 "A" 这将是 Blah.A。怎么可能这样做?

是个 Enum.valueOf() 我需要的方法?如果是这样,我将如何使用它?


1593
2018-03-02 22:56


起源




答案:


是, Blah.valueOf("A") 会给你 Blah.A

请注意,名称必须是 精确 匹配,包括案例: Blah.valueOf("a") 和 Blah.valueOf("A ") 两个都扔了 IllegalArgumentException

静态方法 valueOf() 和 values() 是在编译时创建的,不会出现在源代码中。但它们确实出现在Javadoc中;例如, Dialog.ModalityType 显示两种方法。


1858
2018-03-02 22:57



供参考, Blah.valueOf("A") 方法是 区分大小写 并且不容忍无关的空白,因此@JoséMi提出了下面提出的替代解决方案。 - Brett
@Michael Myers,既然这个答案是迄今为止投票最多的答案,我是否应该理解定义枚举及其String值完全相同是一种好习惯? - Kevin Meredith
@KevinMeredith:如果你的意思是 toString() 价值,不,我不会这么说。 name()将获取枚举常量的实际定义名称,除非您覆盖它。 - Michael Myers♦
究竟是什么意思“是在编译时创建的,不会出现在源代码中。” ? - treesAreEverywhere
@treesAreEverywhere更具体地说,那些方法是 产生 (要么 综合)由编译器。实际上 enum Blah {...} 定义不应该试图声明自己的 values 也不 valuesOf。这就像你怎么写“AnyTypeName.class”,即使你从未真正声明过“类”成员变量;编译器使它全部正常工作。 (这个答案可能在3个月之后对你不再有用,但为了以防万一。) - Ti Strga


如果文本与枚举值不同,则为另一种解决方案:

public enum Blah {
  A("text1"),
  B("text2"),
  C("text3"),
  D("text4");

  private String text;

  Blah(String text) {
    this.text = text;
  }

  public String getText() {
    return this.text;
  }

  public static Blah fromString(String text) {
    for (Blah b : Blah.values()) {
      if (b.text.equalsIgnoreCase(text)) {
        return b;
      }
    }
    return null;
  }
}

711
2018-06-03 10:57



throw new IllegalArgumentException("No constant with text " + text + " found") 会比。更好 return null。 - whiskeysierra
@whiskeysierra Jon Skeet不同意这一点。 stackoverflow.com/questions/1167982/... - Sanghyun Lee
@Sangdol可以 您 让我们为什么返回null更好? - whiskeysierra
@Sangdol通常检查SUN - oops - Oracle在相同的情况下做什么是一件好事。并作为 Enum.valueOf() 正在展示它 IS 在这种情况下抛出异常的最佳实践。 因为 这是一个特例。 “性能优化”是编写不可读代码的不良借口;-) - raudi
那么,您也可以使用@Nullable注释使其“可读”;-) - JoséMi


这是我使用的一个漂亮的实用程序:

/**
 * A common method for all enums since they can't have another base class
 * @param <T> Enum type
 * @param c enum type. All enums must be all caps.
 * @param string case insensitive
 * @return corresponding enum, or null
 */
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
    if( c != null && string != null ) {
        try {
            return Enum.valueOf(c, string.trim().toUpperCase());
        } catch(IllegalArgumentException ex) {
        }
    }
    return null;
}

然后在我的枚举类中,我通常会这样做以节省一些输入:

public static MyEnum fromString(String name) {
    return getEnumFromString(MyEnum.class, name);
}

如果您的枚举不是全部大写,只需更改 Enum.valueOf 线。

太糟糕了我无法使用 T.class 对于 Enum.valueOf 如 T 被删除了。


98
2017-11-12 15:52



那个空的挡板确实让我疯了,抱歉。 - whiskeysierra
@LazloBonin:异常是针对特殊情况而非控制流程。给自己一份 有效的Java。 - Martin Schröder
如果您要使用的Java API抛出异常并且您不希望您的代码抛出异常,您可以像这样吞下异常,或者从头开始重写逻辑,这样就不会抛出异常。吞噬异常通常是较小的邪恶。 - Nate C-K
可怕!总是, 总是 捕获可以处理它们的异常。上面的例子就是一个很好的例子 怎么不这样做。为什么?所以它返回NULL,然后调用者必须检查NULL或抛出NPE。如果调用者知道如何处理这种情况,那么执行if与try-catch可能看起来更优雅, 但 如果他无法处理,他必须再次传递null和调用者的调用者 再次 必须检查NULL等等。 - raudi
为了公平对待上面的解决方案,确实有一些用例要求您返回null而不是抛出IllegalArgumentException并打破程序流,例如,在Web服务模式和数据库模式之间映射枚举,其中它们并不总是一个-to-之一。但是,我同意不应该将catch块留空。放一些像log.warn这样的代码用于跟踪目的。 - Adrian M


你也应该小心你的情况。让我解释一下:做 Blah.valueOf("A") 工作,但 Blah.valueOf("a") 不管用。然后又来了 Blah.valueOf("a".toUpperCase(Locale.ENGLISH)) 会工作。

编辑
toUpperCase 至 toUpperCase(Locale.ENGLISH) 基于 TC。评论 和 java文档

EDIT2 在Android上你应该使用 Locale.US作为 苏莱指出


66
2018-05-09 16:33



警惕默认的语言环境! - tc.
对于那里的Android用户,我想指出这一点 Android文档 明确鼓励使用 Locale.US 用于机器可读输入/输出。 - sulai
不同地方的大写不一样吗? - Holloway
@Trengot 是。 - João Portela
@Trengot不幸的是,是的。土耳其是一个很好的例子。将此与Java对默认字符集的破坏处理(默认为Windows上的拉丁语而不是Unicode)相结合,你会发现使用接受字符集或语言环境的默认版本方法几乎​​总是不安全的。您应该几乎总是明确定义它们。 - Stijn de Witt


使用Joshua Bloch的图案, 有效的Java

(为简洁起见,简化)

enum MyEnum {
  ENUM_1("A"),
  ENUM_2("B");

  private String name;

  private static final Map<String,MyEnum> ENUM_MAP;

  MyEnum (String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }

  // Build an immutable map of String name to enum pairs.
  // Any Map impl can be used.

  static {
    Map<String,MyEnum> map = new ConcurrentHashMap<String,MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
      map.put(instance.getName(),instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
  }

  public static MyEnum get (String name) {
    return ENUM_MAP.get(name);
  }
}

另见:

使用Enum和实例映射的Oracle Java示例

Enum类型中静态块的执行顺序

如何从String值中查找Java枚举


38
2018-06-15 16:37



若约书亚布洛赫说,那么这是唯一的出路:-)。遗憾的是我总是要向下滚动。 - dermoritz
这在Java 8中甚至更简单,因为你可以做到:Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity())) 我还建议重写toString()(通过构造函数传入)并使用它而不是名称,特别是如果Enum与可序列化数据相关联,因为这可以让你控制外壳而不给Sonar一个合适的。 - Novaterata
Java 8当然可以/将在这个论坛上改变很多(更好的)答案。不确定是否有尾巴(声纳)摇尾巴(应用程序代码)。 - Darrell Teague
如果你打算把它放进去 unmodifiableMap 无论如何,那么从一开始就没有任何好处 ConcurrentHashMap。只需使用一个 HashMap。 (如果你有番石榴的话 ImmutableMap 然后我建议改为!) - Daniel Pryden
谷歌的ImmutableMap肯定是一个更好的(真正的不可改变的)实现,但为了演示一个唯一的核心Java解决方案......并专注于字符串映射查找案例,只使用了核心Java类。通常,已经发现ConcurrentHashMap几乎总是优先于HashMap,因为在必须考虑并发修改时,总体性能配置文件(添加,删除,修改)和简单性与包装/锁定HashMap相比。 - Darrell Teague


这是一个可以为任何枚举执行此操作的方法,并且不区分大小写。

/** 
 * Finds the value of the given enumeration by name, case-insensitive. 
 * Throws an IllegalArgumentException if no match is found.  
 **/
public static <T extends Enum<T>> T valueOfIgnoreCase(
        Class<T> enumeration, String name) {

    for (T enumValue : enumeration.getEnumConstants()) {
        if (enumValue.name().equalsIgnoreCase(name)) {
            return enumValue;
        }
    }

    throw new IllegalArgumentException(String.format(
        "There is no value with name '%s' in Enum %s",
        name, enumeration.getName()
    ));
}

34
2017-12-01 21:53



这种变化正确地做到了: equalsIgnoreCase 是要走的路。 +1 - Stijn de Witt


运用 Blah.valueOf(string) 是最好的,但你可以使用 Enum.valueOf(Blah.class, string) 同样。


28
2018-05-09 17:23



区分大小写,没有帮助! - Murtaza Kanchwala
@MurtazaKanchwala你能澄清一下你的评论吗?你想做什么? - Peter Lawrey
嗨@PeterLawrey,我想从String public enum ObjectType {PERSON(“Person”)public String parameterName中获取一个枚举; ObjectType(String parameterName){this.parameterName = parameterName; public String getParameterName(){return this.parameterName; public static ObjectType fromString(String parameterName){if(parameterName!= null){for(ObjectType objType:ObjectType.values()){if(parameterName.equalsIgnoreCase(objType.parameterName)){return objType;返回null; }} - Murtaza Kanchwala


如果您不想编写自己的实用程序,请使用Google  图书馆:

Enums.getIfPresent(Blah.class, "A")

与内置的java函数不同,它让你检查Blah中是否存在A并且不会抛出异常。


22
2018-04-02 20:12



可悲的是,这返回一个google Optional而不是java Optional - javaProgrammer


您可能需要这样:

public enum ObjectType {
    PERSON("Person");

    public String parameterName;

    ObjectType(String parameterName) {
        this.parameterName = parameterName;
    }

    public String getParameterName() {
        return this.parameterName;
    }

    //From String method will return you the Enum for the provided input string
    public static ObjectType fromString(String parameterName) {
        if (parameterName != null) {
            for (ObjectType objType : ObjectType.values()) {
                if (parameterName.equalsIgnoreCase(objType.parameterName)) {
                    return objType;
                }
            }
        }
        return null;
    }
}

还有一个补充:

   public static String fromEnumName(String parameterName) {
        if (parameterName != null) {
            for (DQJ objType : DQJ.values()) {
                if (parameterName.equalsIgnoreCase(objType.name())) {
                    return objType.parameterName;
                }
            }
        }
        return null;
    }

这将通过字符串化的枚举名称返回值,例如如果您在fromEnumName中提供“PERSON”,它将返回Enum的值,即“Person”


13
2017-07-02 11:54





在Java 8或更高版本中,使用

public enum Blah
{
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Blah fromText(String text) {
        return Arrays.stream(values())
          .filter(bl -> bl.text.equalsIgnoreCase(text))
          .findFirst()
          .orElse(null);
    }
}

11
2017-08-01 10:17





另一种方法是使用隐式静态方法 name() 恩欧姆name将返回用于创建该枚举的确切字符串,该字符串可用于检查提供的字符串:

public enum Blah {

    A, B, C, D;

    public static Blah getEnum(String s){
        if(A.name().equals(s)){
            return A;
        }else if(B.name().equals(s)){
            return B;
        }else if(C.name().equals(s)){
            return C;
        }else if (D.name().equals(s)){
            return D;
        }
        throw new IllegalArgumentException("No Enum specified for this string");
    }
}

测试:

System.out.println(Blah.getEnum("B").name()); 

//it will print B  B

灵感: 10 Java中的枚举示例


10
2017-11-29 05:02



这基本上是什么 valueOf 为你做。这种静态方法不提供任何额外的,例外的等等。那么if / else结构是非常危险的...添加任何新的枚举常量将导致此方法无变化地中断。 - YoYo
还要考虑我们如何使用valueOf进行不区分大小写的查找,或者我们如何避免它的异常并使用别名来提供替代名称的示例: stackoverflow.com/a/12659023/744133 - YoYo
name() 不是一成不变的。 - nrubin29