如何从其 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 -> 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<>
被证明比 foo 循环快 40% 以上。【参考方案9】:
您可以按照上面 Gareth Davis 和 Brad Mace 的建议使用 Enum::valueOf()
函数,但请确保处理 IllegalArgumentException
,如果使用的字符串不存在于枚举中,则会抛出该函数。
【讨论】:
【参考方案10】:也许,看看这个。它为我工作。
这样做的目的是用“/red_color”查找“RED”。
如果enum
s 很多,则声明static map
并将enum
s 仅加载一次会带来一些性能优势。
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 枚举?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 apollo graphQL 服务器的枚举值中添加特殊字符?