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


164

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

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初始化之前进行构造的)。通常的解决方案是使用嵌套类。
Tom Hawtin-大头钉

谢谢大家如此迅速的回应。(FWIW,我发现Sun Javadocs对于解决此问题不是很有用)。
peter.murray.rust,2009年

这实际上是语言问题,而不是图书馆问题。但是,我认为对API文档的阅读要比对JLS的阅读要多(尽管可能不是语言设计者阅读的),所以类似的事情在java.lang文档中应该会更加突出。
Tom Hawtin-大头钉

Answers:


241

使用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烦恼,它会足够快。


谢谢-如果我知道值仍然不同,我可以使用大小写转换
peter.murray.rust

您可以直接访问类成员“ abbr”,因此可以使用“ v.abbr.equals ...”代替“ v.abbr()”。
Amio.io 2015年

7
另外,对于任意值(第二种情况),请考虑引发一个IllegalArgumentException而不是返回null(即,当未找到匹配项时)- Enum.valueOf(..)不管怎么说,这就是JDK的方式。
Priidu Neemre

135

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

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

4
由于类加载器问题,此方法无法可靠运行。我不建议这样做,而且我经常看到它失败,因为在访问之前查找映射尚未准备就绪。您需要将“查找”放在枚举之外或放在另一个类中,以便它首先加载。
亚当·根特

7
@Adam Gent:下面有一个到JLS的链接,其中以这个确切的结构为例。它说静态初始化从上到下发生:docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#d5e12267
Selena

3
是的,像Java 8这样的现代Java已修复了内存模型。话虽如此,如果您由于循环引用而嵌入了初始化,则像jrebel这样的热插拔代理仍然会陷入困境。
亚当·根特

@亚当·根特:嗯。我每天都学到新东西。[安静地进行引导以使用引导程序单例重构我的枚举...]很抱歉,如果这是一个愚蠢的问题,但这主要是静态初始化问题吗?
塞莱娜2014年

我从未见过静态代码块未初始化的问题,但是自从我从2015年开始使用Java以来​​,我只使用过Java8。我只是使用JMH 1.22做了一个基准测试,并使用a HashMap<>来存储枚举被证明是比foor loop快40%以上。
cbaldan

21

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

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

我会抛出一个异常而不是返回null。但这也取决于用例
亚历山大

12

@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,并且(理论上)应该在此之前加载。


3
需要定义“危险”以及内部类实现为何重要的原因。这更多的是自上而下的静态定义问题,但另一个相关答案中包含工作代码,并提供指向此问题的链接,该问题使用Enum实例本身上的静态Map,并使用“ get”方法从用户定义的值枚举类型。stackoverflow.com/questions/604424/lookup-enum-by-string-value
Darrell Teague

2
@DarrellTeague最初的问题是由于旧的JVM(1.6版之前)或其他JVM(IBM)或热代码交换程序(JRebel)引起的。在现代JVM中不应发生此问题,因此我可以删除答案。
亚当·根特

值得一提的是,由于自定义编译器实现和运行时优化,的确有可能...排序可能会重新排序,从而导致错误。但是,这似乎违反了规范。
Darrell Teague

7

而且您不能使用valueOf()吗?

编辑:顺便说一句,没有什么可以阻止您在枚举中使用static {}。


valueOfenum类上的综合类,因此您无需指定enum类Class
Tom Hawtin-大头钉

当然,它只是没有javadoc条目,因此很难链接。
Fredrik的

1
您可以使用valueOf(),但名称必须与枚举声明中设置的名称完全匹配。可以将上述两种查找方法中的任何一种修改为使用.equalsIgnoringCase(),并且对错误的鲁棒性更高。

@leonardo真的。如果它增加了鲁棒性,或者仅仅是容错就值得商bat。在大多数情况下,我会说是后者,在那种情况下,最好在其他地方处理它,但仍使用Enum的valueOf()。
Fredrik 2012年

4

为了帮助其他人,我更喜欢的选项(未在此处列出)使用了Guava的地图功能

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 IllegalArgumentExceptionfromString可以返回Optional,无论您喜欢哪种行为。


3

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

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

+1出色地使用流,并且非常简洁而不牺牲可读性!我以前也没有见过这种用法Function.identity(),我发现它在文字上比更具吸引力e -> e
松Q.

0

也许,看看这个。它为我工作。这样做的目的是用'/ red_color'查找'RED'。如果声明多个a,static map并且enum仅将其声明一次并将其加载到其中,将会带来一些性能上的好处enum

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

请输入您的观点。


-1

您可以将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();
   }
};

请参阅以下链接以获取更多说明


-1

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

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

如果您需要在辅助值上使用它,则可以像在其他一些答案中一样首先构建查找映射。


-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

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

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.