如何从其 String 值中查找 Java 枚举?

Posted

技术标签:

【中文标题】如何从其 String 值中查找 Java 枚举?【英文标题】:How can I lookup a Java enum from its String value? 【发布时间】:2010-11-08 00:43:38 【问题描述】:

我想从它的字符串值(或任何其他值)中查找一个枚举。我已经尝试了以下代码,但它不允许在初始化程序中使用静态。有没有简单的方法?

public enum Verbosity 

    BRIEF, NORMAL, FULL;

    private static Map<String, Verbosity> stringMap = new HashMap<String, Verbosity>();

    private Verbosity() 
        stringMap.put(this.toString(), this);
    

    public static Verbosity getVerbosity(String key) 
        return stringMap.get(key);
    
;

【问题讨论】:

IIRC,这给出了 NPE,因为静态初始化是自上而下完成的(即顶部的枚举常量是在它下降到 stringMap 初始化之前构造的)。通常的解决方案是使用嵌套类。 感谢大家如此迅速的反应。 (FWIW 我没有发现 Sun Javadocs 对这个问题很有用)。 这确实是语言问题而不是库问题。但是,我认为 API 文档的阅读量比 JLS 多(尽管可能不是由语言设计者阅读),所以这样的内容在 java.lang 文档中应该更突出。 【参考方案1】:
public enum EnumRole 

ROLE_ANONYMOUS_USER_ROLE ("anonymous user role"),
ROLE_INTERNAL ("internal role");

private String roleName;

public String getRoleName() 
    return roleName;


EnumRole(String roleName) 
    this.roleName = roleName;


public static final EnumRole getByValue(String value)
    return Arrays.stream(EnumRole.values()).filter(enumRole -> enumRole.roleName.equals(value)).findFirst().orElse(ROLE_ANONYMOUS_USER_ROLE);


public static void main(String[] args) 
    System.out.println(getByValue("internal role").roleName);

【讨论】:

【参考方案2】:

如果您想要一个默认值并且不想构建查找映射,您可以创建一个静态方法来处理它。 此示例还处理预期名称以数字开头的查找。

    public static final Verbosity lookup(String name) 
        return lookup(name, null);
    

    public static final Verbosity lookup(String name, Verbosity dflt) 
        if (StringUtils.isBlank(name)) 
            return dflt;
        
        if (name.matches("^\\d.*")) 
            name = "_"+name;
        
        try 
            return Verbosity.valueOf(name);
         catch (IllegalArgumentException e) 
            return dflt;
        
    

如果您需要它作为次要值,您只需像其他一些答案一样首先构建查找图。

【讨论】:

【参考方案3】:

从 java 8 开始,您可以在一行中初始化地图,而无需静态块

private static Map<String, Verbosity> stringMap = Arrays.stream(values())
                 .collect(Collectors.toMap(Enum::toString, Function.identity()));

【讨论】:

+1 对流的出色使用,非常简洁而不牺牲可读性!我之前也没有见过Function.identity() 的这种用法,我发现它在文字上比e -&gt; e 更具吸引力。【参考方案4】:

如果对他人有帮助,我更喜​​欢的选项(此处未列出)使用Guava's Maps functionality:

public enum Vebosity 
    BRIEF("BRIEF"),
    NORMAL("NORMAL"),
    FULL("FULL");

    private String value;
    private Verbosity(final String value) 
        this.value = value;
    

    public String getValue() 
        return this.value;
    

    private static ImmutableMap<String, Verbosity> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(Verbosity.values()), Verbosity::getValue);

    public static Verbosity fromString(final String id) 
        return reverseLookup.getOrDefault(id, NORMAL);
    

默认情况下,您可以使用null,您可以使用throw IllegalArgumentException 或您的fromString 可以返回Optional,无论您喜欢什么行为。

【讨论】:

【参考方案5】:

使用 Java 8,您可以通过以下方式实现:

public static Verbosity findByAbbr(final String abbr)
    return Arrays.stream(values()).filter(value -> value.abbr().equals(abbr)).findFirst().orElse(null);

【讨论】:

我会抛出异常而不是返回null。但这也取决于用例【参考方案6】:

您可以将 Enum 定义为以下代码:

public enum Verbosity 

   BRIEF, NORMAL, FULL, ACTION_NOT_VALID;
   private int value;

   public int getValue()
   
     return this.value;
    

   public static final Verbosity getVerbosityByValue(int value)
   
     for(Verbosity verbosity : Verbosity.values())
     
        if(verbosity.getValue() == value)
            return verbosity ;
     

     return ACTION_NOT_VALID;
   

   @Override
   public String toString()
   
      return ((Integer)this.getValue()).toString();
   
;

See following link for more clarification

【讨论】:

【参考方案7】:

@Lyle 的回答相当危险,我发现如果您将枚举设为静态内部类,它尤其不起作用。相反,我使用了类似的东西,它将在枚举之前加载 BootstrapSingleton 映射。

编辑 现代 JVM(JVM 1.6 或更高版本)应该不再是问题,但我确实认为 JRebel 仍然存在问题,但我没有机会重新测试它

先加载我:

   public final class BootstrapSingleton 

        // Reverse-lookup map for getting a day from an abbreviation
        public static final Map<String, Day> lookup = new HashMap<String, Day>();
   

现在在枚举构造函数中加载它:

   public enum Day  
        MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
        THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

        private final String abbreviation;

        private Day(String abbreviation) 
            this.abbreviation = abbreviation;
            BootstrapSingleton.lookup.put(abbreviation, this);
        

        public String getAbbreviation() 
            return abbreviation;
        

        public static Day get(String abbreviation) 
            return lookup.get(abbreviation);
        
    

如果你有一个内部枚举,你可以在枚举定义之上定义 Map 并且(理论上)应该在之前加载。

【讨论】:

需要定义“危险”以及为什么内部类实现很重要。这更像是一个自上而下的静态定义问题,但在另一个相关答案中有工作代码,并链接回这个问题,该问题在 Enum 实例本身上使用静态 Map,并使用从用户定义的值到的“get”方法枚举类型。 ***.com/questions/604424/lookup-enum-by-string-value @DarrellTeague 最初的问题是由于旧的 JVM(1.6 之前)或其他 JVM(IBM)或热代码交换器(JRebel)造成的。这个问题不应该发生在现代 JVM 中,所以我可能会删除我的答案。 请注意,由于自定义编译器实现和运行时优化确实有可能...排序可能会重新排序,从而导致错误。但是,这似乎违反了规范。【参考方案8】:

你已经接近了。对于任意值,请尝试以下操作:

public enum Day  

    MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
    THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

    private final String abbreviation;

    // Reverse-lookup map for getting a day from an abbreviation
    private static final Map<String, Day> lookup = new HashMap<String, Day>();

    static 
        for (Day d : Day.values()) 
            lookup.put(d.getAbbreviation(), d);
        
    

    private Day(String abbreviation) 
        this.abbreviation = abbreviation;
    

    public String getAbbreviation() 
        return abbreviation;
    

    public static Day get(String abbreviation) 
        return lookup.get(abbreviation);
    

【讨论】:

由于类加载器问题,此方法无法可靠运行。我不推荐它,而且我经常看到它失败,因为查找图在被访问之前还没有准备好。您需要将“查找”放在枚举之外或另一个类中,以便它首先加载。 @Adam Gent:下面有一个指向 JLS 的链接,其中以这个确切的结构为例。它说静态初始化从上到下发生:docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#d5e12267 是的,像 java 8 这样的现代 java 已经修复了内存模型。话虽如此,如果由于循环引用而嵌入初始化,像 jrebel 这样的热插拔代理仍然会搞砸。 @Adam Gent:嗯。我每天都学到新东西。 [悄悄地用引导单例重构我的枚举...]对不起,如果这是一个愚蠢的问题,但这主要是静态初始化的问题吗? 我从未见过静态代码块未初始化的问题,但自 2015 年开始使用 Java 以来,我只使用过 Java 8。我只是使用 JMH 1.22 做了一个小基准测试并使用用于存储枚举的 HashMap&lt;&gt; 被证明比 foo 循环快 40% 以上。【参考方案9】:

您可以按照上面 Gareth Davis 和 Brad Mace 的建议使用 Enum::valueOf() 函数,但请确保处理 IllegalArgumentException,如果使用的字符串不存在于枚举中,则会抛出该函数。

【讨论】:

【参考方案10】:

也许,看看这个。它为我工作。 这样做的目的是用“/red_color”查找“RED”。 如果enums 很多,则声明static map 并将enums 仅加载一次会带来一些性能优势。

public class Mapper 

public enum Maps 

    COLOR_RED("/red_color", "RED");

    private final String code;
    private final String description;
    private static Map<String, String> mMap;

    private Maps(String code, String description) 
        this.code = code;
        this.description = description;
    

    public String getCode() 
        return name();
    

    public String getDescription() 
        return description;
    

    public String getName() 
        return name();
    

    public static String getColorName(String uri) 
        if (mMap == null) 
            initializeMapping();
        
        if (mMap.containsKey(uri)) 
            return mMap.get(uri);
        
        return null;
    

    private static void initializeMapping() 
        mMap = new HashMap<String, String>();
        for (Maps s : Maps.values()) 
            mMap.put(s.code, s.description);
        
    


请发表你的意见。

【讨论】:

【参考方案11】:

使用为每个 Enum 自动创建的 valueOf 方法。

Verbosity.valueOf("BRIEF") == Verbosity.BRIEF

对于任意值以:

开头
public static Verbosity findByAbbr(String abbr)
    for(Verbosity v : values())
        if( v.abbr().equals(abbr))
            return v;
        
    
    return null;

只有在探查器告诉您之后才继续执行 Map。

我知道它会遍历所有值,但只有 3 个枚举值几乎不值得付出任何其他努力,事实上,除非你有很多值,否则我不会为 Map 而烦恼,它会足够快。

【讨论】:

谢谢 - 如果我知道值仍然不同,我可以使用大小写转换 您可以直接访问类成员“abbr”,因此可以使用“v.abbr.equals...”代替“v.abbr()”。 另外,对于任意值(第二种情况)考虑抛出 IllegalArgumentException 而不是返回 null (ie 找不到匹配项) - 这就是 JDK @无论如何,987654325@ 做到了。 @Gareth Davis 我认为EnumSet.allOf(Verbosity.class) 可能比values() 更好,后者每次都会在堆上创建一个新数组! @Adam Gent 反过来会在堆上创建一个对象......非常怀疑你是否会注意到除了非常敏感的代码之外的任何差异【参考方案12】:

你不能使用valueOf()?

编辑:顺便说一句,没有什么能阻止你在枚举中使用 static 。

【讨论】:

或者枚举类上的合成valueOf,所以不需要指定枚举类Class 当然,它只是没有 javadoc 条目,所以很难链接到。 您可以使用 valueOf() 但名称必须与枚举声明中设置的名称相同。上述两种查找方法中的任何一种都可以修改为使用 .equalsIgnoringCase() 并且对错误的鲁棒性稍强。 @leonardo 是的。如果它增加了鲁棒性或只是容错是有争议的。在大多数情况下,我会说是后者,在这种情况下,最好在其他地方处理它并仍然使用 Enum 的 valueOf()。

以上是关于如何从其 String 值中查找 Java 枚举?的主要内容,如果未能解决你的问题,请参考以下文章

如何从Java中的字符串值中获取枚举值

如何在 apollo graphQL 服务器的枚举值中添加特殊字符?

从字典集合值中查找所有数据到另一个字典集合键

Enum枚举类型

QThread如何从其自己的线程发送一个带有枚举作为QML消耗参数的信号?

怎么使用java中的枚举方法