簡體   English   中英

當值不存在時,Java Enum.valueOf()效率

[英]Java Enum.valueOf() efficiency when value does not exist

您認為哪種效率更高?

使用'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);
    }
}

更新

為了澄清,輸入字符串將來自客戶端應用程序,而不是用戶。 換句話說,在這個例子中收到非工作日會是一個錯誤。

第二種方法的性能問題是什么? 抓住這樣的例外幾乎沒有任何成本。 從設計角度來看,使用異常進行正常控制流通常是一個壞主意,這是性能考慮的日子早已不復存在。 在調試器中,使用異常作為重要的控制操作會使事情減慢大約10倍。但這會被JIT優化,並且在生產中沒有可測量的影響。

這些數字是基於我對zxing項目進行評估的經驗,該項目使用了各種流量控制的例外情況。 當我第一次看到它時,我感到震驚。 我仍然認為這不是最好的設計,但我做了相當多的測試,可以肯定地說它對性能沒有實際影響。 這是一種在整個地方使用異常進行流量控制的算法。 您的情況,即異常只會在非常特殊的情況下被拋出,這是一個非問題。

編輯:我的答案中有一兩個下注,我想確保我對我所說的內容非常清楚:我不認為對正常控制流使用異常是個好主意。 僅僅因為性能不是不使用異常的好方法,這並不意味着沒有其他完全正確的原因(例如可讀性,可測試性,可擴展性)。 在OP的情況下,絕對要求使用異常,並且肯定不會導致任何類型的性能問題。

我知道這是一個老帖子,但我相信以下結果仍然很有趣。 我運行了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()

正如已經評論過的那樣,您必須進行剖析才能確定。 即使在您自己的解析方法中,您也可以通過在解析列表時返回枚舉來加快速度。

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,因此不會生成堆棧跟蹤。

如果您的問題確實是關於7項目中搜索的效率,那么您已經浪費了太多時間。 即使是最快的搜索算法也會產生零或負的好處,直到N> 15左右,而不是O(1)。

將有效字符串存儲在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);
}

循環不執行任何調用valueof的操作,它們具有相同的功能:檢查字符串是否有效枚舉。 您認為從第一個選項中獲得了什么?

第二種選擇是最好的:

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

或者,您可以在類首次加載時查看枚舉值(請參閱靜態修飾符)並使用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);
}

如果您需要更多詳細信息,請告訴我們

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM