Оптимизируйте, добавив Enum в статический держатель - PullRequest
0 голосов
/ 14 октября 2018

Интересно, если добавление Enum статического держателя всегда лучше, чем итерация значений в методе "get" Enum (или аналогичном для получения конкретного значения Enum).

Например, для Spring HttpStatus текущая реализация:

HttpStatus(int value, String reasonPhrase) {
    this.value = value;
    this.reasonPhrase = reasonPhrase;
}
public static HttpStatus valueOf(int statusCode) {
    for (HttpStatus status : values()) {
        if (status.value == statusCode) {
            return status;
        }
    }
    throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
}

Может быть оптимизировано с помощью:

private static class Holder {
    private static Map<Integer, HttpStatus> enumMap = new HashMap<>();
}

HttpStatus(int value, String reasonPhrase) {
    this.value = value;
    this.reasonPhrase = reasonPhrase;
    Holder.enumMap.put(value, this);
}

public static HttpStatus valueOf(int statusCode) {
     return Holder.enumMap.computeIfAbsent(statusCode, statusCode -> {
            throw new IllegalArgumentException("No matching constant for [" + statusCode + "]"); });
}

Оптимизация кода:

Версия цикла имеет линейную сложность по времени (каждый запросчтобы получить значение), в то время как версия, использующая HashMap, имеет временную сложность O (1).

Может ли эта оптимизация иметь недостаток, который я пропускаю?

Ответы [ 2 ]

0 голосов
/ 14 октября 2018

Я давно проверил этот подход, я решил, что НЕТ ПРЕИМУЩЕСТВ * , чтобы использовать Map вместо значений цикла вручную.

  • Карта происходит в памяти
  • Чтобы сделать карту с O(1) поиском действительно лучше, чем O(n) ручной цикл, enum должен иметь более 1000 констант (это экспериментальное число).Но по моему опыту, у меня были enum Страны с максимумом 200+.
  • В худшем случае, это НЕ БУТЫЛКА всего приложения.

Я всегда использую ручной цикл и не беспокоюсь о производительности.Многие фреймворки сериализации (например, Джексон для преобразования enum в json) используют Enum.valueOf().

0 голосов
/ 14 октября 2018

Следует иметь в виду, что после инициализации статическая карта будет храниться в памяти неограниченное время;циклический подход будет содержать массив, созданный путем вызова values() на время итерации цикла.

Однако стоит отметить, что использование класса-держателя не дает никаких преимуществ: потому что вы добавляетек карте в конструкторе, класс держателя будет сразу же инициализирован при вызове конструктора.

Таким образом, вы также можете использовать простое старое статическое (окончательное) поле.

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

Вместо этого вы можете инициализировать непосредственно в поле:

static final Map<Integer, HttpStatus> map = 
    Collections.unmodifiableMap(
        Stream.of(HttpStatus.values())
            .collect(toMap(s -> s.value,  s -> s)));

(или используйте что-то вроде Guava ImmutableMap)

Еще один момент в подходе инициализации карты напрямую заключается в том, что она не добавляет к карте в конструкторе - что означает, что вы не используетена самом деле не нужно помещать это в сам класс enum.Это дает вам возможность использовать его в перечислениях, в которых у вас нет возможности изменить код, и / или добавлять карту только в те места, где вы обнаружили необходимость в производительности.

...