当值不存在时,Java Enum.valueOf()效率

时间:2011-08-23 17:13:19

标签: java enums

您认为哪种效率更高?

使用'WeekDay'只是一个例子:

public enum WeekDay {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY;
}

循环并首先验证日期字符串:

public void parseString(String line) {
    String[] tokens = line.split();
    String day = tokens[1]; // day 'should' always be a weekday
    if (isValidWeekDay(day)) {
        WeekDay weekDay = WeekDay.valueOf(day); // won't throw exception
        ...
    } else {
        throw new InvalidWeekDayException(day); // subclass of RuntimeException
    }
}
private boolean isValidWeekDay(String day) {
    for (WeekDay weekDay : WeekDay.values()) {
        if(weekDay.toString().equals(day))
           return true;
    }
    return false;
}

或者因为在99.99%的情况下,白天是正确的:

public void parseString(String line) {
    String[] tokens = line.split();
    String day = tokens[1]; // day 'should' always be a weekday
    try {
        WeekDay weekDay = WeekDay.valueOf(day); // might throw exception
        ...
    } catch (IllegalArgumentException e) {
        throw new InvalidWeekDayException(day, e);
    }
}

更新

为了澄清,输入字符串将来自客户端应用程序,而不是用户。换句话说,在这个例子中收到非工作日会是一个错误。

7 个答案:

答案 0 :(得分:8)

第二种方法的性能问题是什么?抓住这样的例外几乎没有任何成本。从设计角度来看,使用异常进行正常控制流通常是一个坏主意,这是性能考虑的日子早已不复存在。在调试器中,使用异常作为重要的控制操作会使事情减慢约10倍。但这会被JIT优化,并且在生产中没有可测量的影响。

这些数字是基于我对zxing项目进行评估的经验,该项目使用了各种流量控制的例外情况。当我第一次看到它时,我感到震惊。我仍然认为这不是最好的设计,但我做了相当多的测试,可以肯定地说它对性能没有实际影响。这是一种在整个地方使用异常进行流量控制的算法。你的情况只会在非常特殊的情况下被抛出,这是一个非问题。

编辑:我的回答有一两个问题,我想确保我对我所说的内容非常清楚:我不认为使用异常是正常的控制流。仅仅因为性能不是不使用异常的好方法,这并不意味着没有其他完全正确的原因(例如可读性,可测试性,可扩展性)。在OP的情况下,绝对需要使用异常,并且绝对不会导致任何类型的性能问题。

答案 1 :(得分:6)

正如已经评论过的那样,您必须进行剖析才能确定。即使在您自己的解析方法中,您也可以通过在解析列表时返回枚举来加快速度。

private WeekDay getValidWeekDay(String day) {
    for (WeekDay weekDay : WeekDay.values()) {
        if(weekDay.toString().equals(day))
           return weekDay;
    }
    return null;
}

除非这是一个时间关键的应用程序,否则在任何一种情况下我都不会担心,只需采用最易读的方法。我认为这将使用WeekDay.valueOf()方法。

如果您不想处理异常,那么在枚举中创建一个值的Map,并从查找中有效地执行valueOf(),如果找不到则返回null。

public enum WeekDay {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY;

    private static Map<String, WeekDay> valueMap;

    public static WeekDay getValue(String possibleName)
    {
        if (valueMap == null)
        {
            valueMap = new HashMap<String, WeekDay>();
            for(WeedDay day: values())
                valueMap.put(day.toString(), day);
        }
        return valueMap.get(possibleName);

    }
 }

这实际上是valueOf()方法正在做的事情,除非它在找不到时抛出IllegalArgumentException。这种方法只会返回null,因此不会生成堆栈跟踪。

答案 2 :(得分:6)

我知道这是一个旧帖子,但我相信以下结果仍然会很有趣。我运行10000000次测试,使用JDK 1.8在enum ENUM {FIRST, SECOND, THIRD, FOURTH, LAST}中查找元素。下表显示了简单循环和valueOf()所需的时间。

text     loop   valueOf  ratio
------------------------------
"FIRST"  121    65       186%
"LAST"   188    57       330%
"foo"    155    8958     1.7%

结论 - 如果我希望值与枚举不匹配,我就不会使用valueOf()

答案 3 :(得分:3)

如果您的问题确实是关于7项目中搜索的效率,那么您已经浪费了太多时间。即使是最快的搜索算法也会产生零或负的好处,直到N> 0。除了O(1)之外,大约15个。

答案 4 :(得分:2)

将有效字符串存储在HashSet中,并根据Set.contains(...)确定字符串是否为有效日期。

该集合可以是static final Set,您可以用不可修改的方式包装以获得良好的衡量标准:

private static final Map<String> WEEKDAY_STRINGS;
static {
  HashSet<String> set = new HashSet();
  for (WeekDay d : WeekDay.values()) {
    set.add(d.toString());
  }
  WEEKDAY_STRINGS = Collections.unmodifiableSet(set);
}

答案 5 :(得分:2)

循环不执行任何调用valueof的操作,它们具有相同的功能:检查字符串是否有效枚举。您认为从第一个选项中获得了什么?

第二种选择是最好的:

 try {
     WeekDay weekDay = WeekDay.valueOf(day); // might throw exception
        ...
    } catch (IllegalArgumentException e) {
        throw new InvalidWeekDayException(day);
    }

答案 6 :(得分:2)

或者,您可以在类首次加载时查看枚举值(请参阅静态修饰符)并使用get()进行验证,如下所示:

private String dayName;
private static final Map<String,Weekday> lookup = new HashMap<String, Weekday>();
static{
    for (Weekday day: values()){
        lookup.put(day.dayName, d);
    }
}
public static Weekday get(String _name){
    return lookup.get(_name);
}

如果您需要更多详细信息,请与我们联系