Java Enum.valueOf() эффективность, когда значение не существует

Что вы считаете более эффективным?

Использование "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);
    }
}

Обновить:

Чтобы уточнить, входная строка будет поступать из клиентского приложения, а не из пользователя. Другими словами, в этом примере было бы ошибкой получить нерабочий день.

Ответ 1

Я знаю его старый пост, но я считаю, что следующий результат будет интересен. Я запускаю 10000000 тестов, чтобы найти элемент в enum ENUM {FIRST, SECOND, THIRD, FOURTH, LAST} с помощью JDK 1.8. В приведенной ниже таблице указано время, требуемое простым циклом и valueOf().

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

Заключение. Я бы не использовал valueOf(), если я ожидаю, что значения не соответствуют перечислению.

Ответ 2

Какова проблема производительности в отношении второго подхода? Захват такого исключения стоит почти ничего. Использование исключений для нормального потока управления, как правило, является плохой идеей с точки зрения дизайна, дни, когда это было соображение эффективности, давно прошли. В отладчике использование исключений в качестве значимых управляющих операций замедляет работу примерно в 10 раз. Но это оптимизируется JIT, и в производстве нет заметного влияния.

Эти цифры основаны на опыте с оценкой, которую я сделал в проекте zxing, который использует исключения для всех видов управления потоком. Когда я впервые увидел это, я был в ужасе. Я все еще думаю, что это не лучший дизайн, но я провел довольно много испытаний и могу с большой долей уверенности сказать, что это не оказало реального влияния на производительность. И это алгоритм, который использует исключения для управления потоком. Ваша ситуация, когда исключение попадает только в исключительных обстоятельствах, не является проблемой.

Изменить: у меня на моем ответе был второй или второй вопрос, и я хочу убедиться, что я очень четко понимаю, что я говорю: я не думаю, что это хорошая идея использовать исключения для нормального контроль потока. Просто потому, что производительность не является хорошим аргументом в пользу исключения исключений, этот способ не означает, что нет других, вполне обоснованных причин (таких как читаемость, тестируемость, расширяемость). В случае OP, использование исключения абсолютно вызвано и, безусловно, не вызовет каких-либо проблем с производительностью.

Ответ 3

Как уже было прокомментировано, вам нужно будет профиль, чтобы узнать наверняка. Даже в вашем собственном синтаксическом подходе вы можете сделать это быстрее, возвращая перечисление при анализе списка.

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

Если это не критическая часть приложения, я бы не стал беспокоиться об этом в любом случае и просто взять наиболее читаемый подход. Я думаю, что будет использовать метод WeekDay.valueOf().

Если вам не нужно иметь дело с исключениями, тогда создайте карту своих значений в enum и эффективно выполните эквивалент 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, поэтому не генерирует stacktrace.

Ответ 4

Если ваш вопрос действительно об эффективности поиска среди 7 пунктов, вы уже потратили слишком много времени на него. Даже самые быстрые алгоритмы поиска дают нулевые или отрицательные преимущества до N > 15 или около того, кроме O (1).

Ответ 5

Сохраните допустимые строки в 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);
}

Ответ 6

Цикл не делает ничего, что вызывающий valueof не имеет, они имеют одинаковую функциональность: проверка правильности строки. Как вы думаете, что вы получаете от первого варианта?

Второй вариант лучше:

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

Ответ 7

Или вы можете создать поиск значений enum внутри вашего перечисления при первом загрузке класса (см. статический модификатор) и выполнить проверку с помощью 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);
}

Сообщите мне, если вам нужна дополнительная информация