Enum.values ​​() против EnumSet.allOf (). Какой из них более предпочтителен? - PullRequest
56 голосов
/ 17 марта 2010

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

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

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

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

Более конкретно, какую форму итератора for следует использовать:

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

или

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

Ответы [ 6 ]

87 голосов
/ 18 марта 2010

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

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

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.util.Collection для передачи, 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 абсолютно необходимо.

12 голосов
/ 17 марта 2010

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

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

7 голосов
/ 18 марта 2010

Существует также Class.getEnumConstants()

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

4 голосов
/ 17 марта 2010

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

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

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

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

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

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

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

0 голосов
/ 02 февраля 2016

EnumSet не предназначен для перебора его значений. Скорее это реализовано с идеей для того, чтобы он представлял BitMap или BitMask эффективно (или достаточно эффективно). Javadoc на EnumSet также заявляет:

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

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

Теперь, вероятно, также верно, что вы можете выполнить то же самое и быстрее, используя битовые маски в стиле C (x ^ 2), однако он предлагает более интуитивно понятный стиль кодирования и безопасное использование типов с использованием перечислений, и расширяет легко превышает размер того, что может содержать 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));
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...