2009-07-03 89 views
113

我想从它的字符串值(或可能的任何其他值)查找一个枚举。我试过下面的代码,但它不允许在初始化程序中使用静态代码。有一个简单的方法吗?如何从字符串值查找Java枚举?

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

IIRC,它给出了一个NPE,因为静态初始化是自顶向下完成的(即顶部的枚举常量在构造到stringMap初始化之前被构造)。通常的解决方案是使用嵌套类。 – 2009-07-03 21:36:59

+0

谢谢大家如此快速的回应。 (FWIW我没有发现Sun Javadocs对这个问题非常有用)。 – 2009-07-03 21:45:30

+0

这实际上是一个语言问题,而不是一个图书馆问题。不过,我认为API文档比JLS更多地阅读(尽管可能不是语言设计者),所以像这样的东西在java.lang文档中可能会更突出。 – 2009-07-03 22:37:50

回答

171

使用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个枚举值是不值得任何其他努力,事实上,除非你有很多值,我不会打扰一个映射它会足够快。

6

而且你不能使用valueOf()

编辑:顺便说一下,没有什么在枚举使用静态{}阻止你。

+0

或者enum类中的合成`valueOf`,所以你不需要指定枚举类`Class`。 – 2009-07-03 21:34:47

+0

当然,它只是没有javadoc条目,所以很难链接到。 – Fredrik 2009-07-03 22:03:17

+1

你可以链接到JLS:http://java.sun.com/docs/books/jls/third_edition/html/classes.html#302265 – newacct 2009-07-04 02:38:25

103

你靠近。对于任意值,你可以试试下面的:

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

@莱尔的回答是相当危险的,我已经看到了,如果你做的枚举静态内部类如果不是特别的工作。相反,我使用了这样的东西,它将在枚举之前加载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); 
     } 
    } 

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

1

也许,看看这个。它为我工作。 这样做的目的是用'/ red_color'查找'RED'。 声明一个static map和加载enum s转换它只有一次会带来一定的性能优势,如果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); 
     } 
    } 
} 
} 

请输入您的意见。

-2

可以使用Enum::valueOf()功能的由加雷斯·戴维斯&布拉德·梅斯上述建议,但一定要处理,如果使用的字符串是不存在的枚举将被抛出的IllegalArgumentException

4

在Java语言规范7有一个a example!这反映了你用自引用初始化映射的问题。

0

您可以定义枚举如下代码:

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

5

与Java 8,你可以用这种方式实现:

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

在情况下,它可以帮助其他人,我喜欢的选项,这里没有列出,使用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,你喜欢的任何行为。