Есть ли снижение производительности при использовании enum.values ​​() по сравнению с массивами String? - PullRequest
18 голосов
/ 15 марта 2010

Я использую перечисления для замены String констант в моем Java-приложении (JRE 1.5).

Есть ли снижение производительности, когда я рассматриваю перечисление как статический массив имен в методе, которыйвызывается постоянно (например, при рендеринге пользовательского интерфейса)?

Мой код выглядит примерно так:

public String getValue(int col) {
  return ColumnValues.values()[col].toString();
}

Уточнения:

  • Я обеспокоенскрытая стоимость, связанная с многократным перечислением values() (например, внутри методов paint ()).
  • Теперь я вижу, что все мои сценарии включают некоторое преобразование int => enum, что не является способом Java.

Какова фактическая цена извлечения массива values()?Это даже проблема?

Android-разработчики

Прочитайте ответ Саймона Лэнхоффа ниже, на который ранее Geeks On Hugs указывал в комментариях к принятому ответу.Enum.values() должен сделать защитную копию

Ответы [ 7 ]

16 голосов
/ 15 марта 2010

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

Между тем, если вас беспокоит стоимость вызова метода values() по сравнению с уже имеющейся ссылкой на массив, не беспокойтесь. Вызов метода в Java (сейчас) невероятно быстрый, и всякий раз, когда это действительно имеет значение для производительности, вызов метода все равно будет встроен компилятором.

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


Если вам интересно, почему сравнения перечислений могут выполняться быстрее, чем сравнения строк, вот подробности:

Это зависит от того, были ли строки интернированы или нет. Для объектов Enum всегда есть только один экземпляр каждого значения перечисления в системе, и поэтому каждый вызов Enum.equals() может быть выполнен очень быстро, как если бы вы использовали оператор == вместо equals() метод. На самом деле, с Enum объектами безопаснее использовать == вместо equals(), тогда как не безопасно делать со строками.

Для строк, если строки были интернированы, то сравнение выполняется так же быстро, как и для Enum. Однако, если строки не были интернированы, то метод String.equals() фактически должен обходить список символов в обеих строках до тех пор, пока одна из строк не закончится или не обнаружит символ, который отличается между двумя строками.

Но, опять же, это, вероятно, не имеет значения, даже в коде рендеринга Swing, который должен выполняться быстро. : -)


@ Бен Линс указывает, что Enum.values() должен делать защитную копию, поскольку массивы изменчивы, и возможно, вы можете заменить значение в массиве, которое возвращается Enum.values(). Это означает, что вы должны учитывать стоимость этой защитной копии. Однако копирование одного смежного массива, как правило, является быстрой операцией, если предположить, что он реализован «под капотом» с использованием некоторого вызова копирования из памяти, а не наивной итерации по элементам в массиве. Поэтому я не думаю, что это меняет окончательный ответ.

14 голосов
/ 10 декабря 2014

Для перечислений, чтобы сохранить неизменность, они клонируют резервный массив каждый раз, когда вы вызываете метод Values ​​(). Это означает, что это повлияет на производительность. Сколько зависит от вашего конкретного сценария.

Я наблюдал за своим собственным приложением для Android и обнаружил, что этот простой вызов использовал 13,4% процессорного времени! в моем конкретном случае.

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

private final static Protocol[] values = Protocol.values();

После этой небольшой оптимизации мой метод вызвал лишь незначительное 0,0% процессорного времени

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

2 голосов
/ 07 февраля 2017

используйте это:

private enum ModelObject { NODE, SCENE, INSTANCE, URL_TO_FILE, URL_TO_MODEL,
    ANIMATION_INTERPOLATION, ANIMATION_EVENT, ANIMATION_CLIP, SAMPLER, IMAGE_EMPTY,
    BATCH, COMMAND, SHADER, PARAM, SKIN }
private static final ModelObject int2ModelObject[] = ModelObject.values();
2 голосов
/ 15 марта 2010

Если вас беспокоит производительность, измерьте.

Из кода я не ожидаю никаких сюрпризов, но 90% всех предположений о производительности неверны. Если вы хотите быть в безопасности, подумайте о том, чтобы переместить перечисления вверх в вызывающий код (т.е. public String getValue(ColumnValues value) {return value.toString();}).

2 голосов
/ 15 марта 2010

Практическое правило: прежде чем подумать об оптимизации, есть ли у вас какие-либо предположения, что этот код может замедлить работу вашего приложения?

Теперь факты.

enum - это, по большей части, синтаксический сахар, разбросанный по всему процессу компиляции. Как следствие, метод values, определенный для класса enum, возвращает статическую коллекцию (то есть загруженную при инициализации класса) с характеристиками, которые можно считать примерно эквивалентными массиву.

1 голос
/ 14 декабря 2017

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

public enum ExampleEnum {
    value1(1),
    value2(2),
    valueUndefined(Integer.MAX_VALUE);

    private final int enumValue;
    private static Map enumMap;
    ExampleEnum(int value){
       enumValue = value;
    }
    static {
       enumMap = new HashMap<Integer, ExampleEnum>();
       for (ExampleEnum exampleEnum: ExampleEnum.values()) {
           enumMap.put(exampleEnum.value, exampleEnum);
        }
    }
    public static ExampleEnum getExampleEnum(int value) {
        return enumMap.contains(value) ? enumMap.get(value) : valueUndefined;
    }
}
0 голосов
/ 15 марта 2010

Я думаю, что да. И использовать константы удобнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...