Есть ли производительность при использовании enum.values ​​() и String массивов?

Я использую перечисления для замены констант 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() должен сделать защитную копию

Ответы

Ответ 1

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

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

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


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

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

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

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


@Ben Lings указывает, что Enum.values() должен делать защитную копию, поскольку массивы изменяемы, и возможно, вы можете заменить значение в массиве, которое возвращается Enum.values(). Это означает, что вам нужно учитывать стоимость этой защитной копии. Однако копирование одного непрерывного массива, как правило, является быстрой операцией, предполагая, что он реализован "под капотом", используя какой-то вызов для копирования памяти, а не наивно итерируя по элементам в массиве. Итак, я не думаю, что здесь меняет окончательный ответ.

Ответ 2

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

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

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

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

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

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

Ответ 3

Как правило: перед тем как подумать об оптимизации, знаете ли вы, что этот код может замедлить ваше приложение?

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

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

Ответ 4

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

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

Ответ 5

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

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();

Ответ 6

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

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;
    }
}

Ответ 7

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