Перечисление.values () vs EnumSet.все(). Какой из них предпочтительнее?


Я искал под капотом EnumSet.allOf и это выглядит очень эффективно, особенно для перечислений с менее 64 значений.

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

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

так,EnumSet.allOf быть предпочтительнее Enum.values?

более конкретно, какая форма for итератор должен использоваться:

for ( final MyEnum val: MyEnum.values( ) );

или

for ( final MyEnum val: EnumSet.allOf( MyEnum.class ) );
6 55

6 ответов:

поскольку я не получил ответа на свой вопрос, какой из них более эффективен, я решил провести собственное тестирование.

Я проверил итерацию над values(),Arrays.asList( values() ) и EnumSet.allOf( ). Я повторил эти тесты 10,000,000 раз для разных размеров перечислений. Вот результаты теста:

oneValueEnum_testValues         1.328
oneValueEnum_testList           1.687
oneValueEnum_testEnumSet        0.578

TwoValuesEnum_testValues        1.360
TwoValuesEnum_testList          1.906
TwoValuesEnum_testEnumSet       0.797

ThreeValuesEnum_testValues      1.343
ThreeValuesEnum_testList        2.141
ThreeValuesEnum_testEnumSet     1.000

FourValuesEnum_testValues       1.375
FourValuesEnum_testList         2.359
FourValuesEnum_testEnumSet      1.219

TenValuesEnum_testValues        1.453
TenValuesEnum_testList          3.531
TenValuesEnum_testEnumSet       2.485

TwentyValuesEnum_testValues     1.656
TwentyValuesEnum_testList       5.578
TwentyValuesEnum_testEnumSet    4.750

FortyValuesEnum_testValues      2.016
FortyValuesEnum_testList        9.703
FortyValuesEnum_testEnumSet     9.266

это результаты тестов, выполненных из командной строки. Когда я запускал эти тесты из Eclipse, я получил подавляющую поддержку testValues. В основном это было меньше чем EnumSet даже для небольших перечислений. Я считаю, что прирост производительности происходит от оптимизации итератора массива в for ( val : array ) петли.

С другой стороны, как только вам понадобится java.утиль.Коллекция, чтобы передать вокруг,Arrays.asList( ) проигрывает EnumSet.allOf, особенно для небольших перечислений, которые, я считаю, будут большинством в любой заданной кодовой базе.

Итак, я бы сказал, что вы должны использовать

for ( final MyEnum val: MyEnum.values( ) )

но

Iterables.filter(
    EnumSet.allOf( MyEnum.class ),
    new Predicate< MyEnum >( ) {...}
)

и использовать только Arrays.asList( MyEnum.values( ) ) где java.util.List это абсолютно необходимо.

вы должны использовать самый простой и понятный для вас подход. Производительность не должна рассматриваться в большинстве ситуаций.

IMHO: ни один из вариантов не работает очень хорошо, поскольку они оба создают объекты. В первом случае и три во втором. Вы можете построить константу, которая содержит все значения по соображениям производительности.

появилась Class.getEnumConstants()

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

The values() метод более ясен и эффективен, если вы просто хотите перебирать все возможные значения перечисления. Значения кэшируются классом (см. Class.getEnumConstants())

Если вам нужно подмножество значений, вы должны использовать EnumSet. Начинаются с allOf() или noneOf() и добавить или удалить значения или использовать просто of() как вам нужно.

Не то, чтобы я прошел всю реализацию, но мне кажется, что EnumSet.allOf () в основном использует ту же инфраструктуру, что и .значения.)( Так что я ожидал бы EnumSet.allOf () требует некоторых (вероятно, незначительных) дополнительных шагов (см. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6276988).

Мне кажется ясным, что предполагаемое использование foreach for(MyEnum val : MyEnum.values()) Почему это по-другому? Вы только запутаете программиста обслуживания.

I то есть, если вам нужна коллекция, вы должны ее получить. Если вы хотите использовать foreach, массивы достаточно хороши. Я бы даже предпочел массивы при нажатии! Зачем обертывать что-либо с чем-либо, если то, что вы получили (массив) достаточно хорошо? Простые вещи, как правило, быстрее.

в любом случае, Питер Lawrey прав. Не беспокойтесь о производительности этого.. Это достаточно быстро, и есть вероятность, что есть миллион других узких мест, которые делают эту крошечную теоретическую разницу в производительности совершенно неуместной (Однако не вижу его точки "создания объекта". Мне первый пример кажется 100% ОК).

EnumSet не строится с намерением перебирать его значения. Скорее он реализован с идеей для него, чтобы представлять растровое изображение или битовую маску эффективно (или достаточно эффективно). Элемент javadoc on EnumSet также указано:

перечисления представлены внутренне в виде битовых векторов. Это представление весьма компактно и эффективно. Пространственно-временные характеристики этого класса должны быть достаточно хорошими, чтобы позволить его использование в качестве высококачественного, безопасной альтернативой традиционным инт "на основе битовых флагов."Даже массовые операции (такие как containsAll и retainAll) должны выполняться очень быстро, если их аргумент также является набором перечислений.

поскольку только один бит может представлять определенное значение перечисления, он также реализован как Set, а не List.

теперь, вероятно, также верно, что вы можете выполнить то же самое и быстрее, используя битовые маски C-стиля (x^2), однако он предлагает более интуитивное кодирование стиль и тип безопасного использования с использованием перечислений, и он легко расширяется за пределы размера what an int или long может содержать.

таким образом, вы можете проверить, что все биты установлены следующим образом:

public class App {
  enum T {A,B}
  public static void main(String [] args) {
    EnumSet<T> t = EnumSet.of(T.A);
    t.containsAll(EnumSet.allOf(T.class));
  }
}